l3cfg: merge branch 'th/l3cfg-ipv6ll'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/976
This commit is contained in:
Thomas Haller 2021-09-10 13:27:15 +02:00
commit 3a6b3e35da
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
29 changed files with 2117 additions and 464 deletions

View file

@ -2382,6 +2382,8 @@ src_core_libNetworkManagerBase_la_SOURCES = \
src/core/nm-l3-config-data.h \
src/core/nm-l3-ipv4ll.c \
src/core/nm-l3-ipv4ll.h \
src/core/nm-l3-ipv6ll.c \
src/core/nm-l3-ipv6ll.h \
src/core/nm-l3cfg.c \
src/core/nm-l3cfg.h \
src/core/nm-ip-config.c \

View file

@ -51,6 +51,7 @@ libNetworkManagerBase = static_library(
'nm-netns.c',
'nm-l3-config-data.c',
'nm-l3-ipv4ll.c',
'nm-l3-ipv6ll.c',
'nm-l3cfg.c',
'nm-ip-config.c',
'nm-ip4-config.c',

View file

@ -241,7 +241,7 @@ static inline gboolean
nm_ndisc_dad_addr_is_fail_candidate_event(NMPlatformSignalChangeType change_type,
const NMPlatformIP6Address *addr)
{
return !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TEMPORARY)
return !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_SECONDARY)
&& ((change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED)
|| (change_type == NM_PLATFORM_SIGNAL_REMOVED
&& addr->n_ifa_flags & IFA_F_TENTATIVE));
@ -255,7 +255,7 @@ nm_ndisc_dad_addr_is_fail_candidate(NMPlatform *platform, const NMPObject *obj)
addr = NMP_OBJECT_CAST_IP6_ADDRESS(
nm_platform_lookup_obj(platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj));
if (addr
&& (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TEMPORARY)
&& (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_SECONDARY)
|| !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_DADFAILED))) {
/* the address still/again exists and is not in DADFAILED state. Skip it. */
return FALSE;

View file

@ -2877,10 +2877,11 @@ typedef struct {
bool timestamp_is_good : 1;
} HostIdData;
static const HostIdData *volatile host_id_static;
static const HostIdData *
_host_id_get(void)
{
static const HostIdData *volatile host_id_static;
const HostIdData *host_id;
again:
@ -2941,6 +2942,78 @@ nm_utils_host_id_get_timestamp_ns(void)
return _host_id_get()->timestamp_ns;
}
static GArray * nmtst_host_id_stack = NULL;
static GMutex nmtst_host_id_lock;
const HostIdData *nmtst_host_id_static_0 = NULL;
void
nmtst_utils_host_id_push(const guint8 *host_id,
gssize host_id_len,
gboolean is_good,
const gint64 *timestamp_ns)
{
NM_G_MUTEX_LOCKED(&nmtst_host_id_lock);
gs_free char *str1_to_free = NULL;
HostIdData * h;
g_assert(host_id_len >= -1);
if (host_id_len < 0)
host_id_len = host_id ? strlen((const char *) host_id) : 0;
nm_log_dbg(LOGD_CORE,
"nmtst: host-id push: \"%s\" (%zu), is-good=%d, timestamp=%" G_GINT64_FORMAT "%s",
nm_utils_buf_utf8safe_escape(host_id,
host_id_len,
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL,
&str1_to_free),
(gsize) host_id_len,
!!is_good,
timestamp_ns ? *timestamp_ns : 0,
timestamp_ns ? "" : " (not-good)");
if (!nmtst_host_id_stack) {
nmtst_host_id_stack = g_array_new(FALSE, FALSE, sizeof(HostIdData));
nmtst_host_id_static_0 = g_atomic_pointer_get(&host_id_static);
}
h = nm_g_array_append_new(nmtst_host_id_stack, HostIdData);
*h = (HostIdData){
.host_id = nm_memdup(host_id, host_id_len),
.host_id_len = host_id_len,
.timestamp_ns = timestamp_ns ? *timestamp_ns : 0,
.is_good = is_good,
.timestamp_is_good = !!timestamp_ns,
};
g_atomic_pointer_set(&host_id_static, h);
}
void
nmtst_utils_host_id_pop(void)
{
NM_G_MUTEX_LOCKED(&nmtst_host_id_lock);
HostIdData *h;
g_assert(nmtst_host_id_stack);
g_assert(nmtst_host_id_stack->len > 0);
nm_log_dbg(LOGD_CORE, "nmtst: host-id pop");
h = &g_array_index(nmtst_host_id_stack, HostIdData, nmtst_host_id_stack->len - 1);
g_free((char *) h->host_id);
g_array_set_size(nmtst_host_id_stack, nmtst_host_id_stack->len - 1u);
if (!g_atomic_pointer_compare_and_exchange(
&host_id_static,
h,
nmtst_host_id_stack->len == 0u ? nmtst_host_id_static_0
: nm_g_array_last(nmtst_host_id_stack, HostIdData)))
g_assert_not_reached();
}
/*****************************************************************************/
static const UuidData *

View file

@ -247,6 +247,33 @@ const char *const * nm_utils_proc_cmdline_split(void);
gboolean nm_utils_host_id_get(const guint8 **out_host_id, gsize *out_host_id_len);
gint64 nm_utils_host_id_get_timestamp_ns(void);
void nmtst_utils_host_id_push(const guint8 *host_id,
gssize host_id_len,
gboolean is_good,
const gint64 *timestamp_ns);
void nmtst_utils_host_id_pop(void);
static inline void
_nmtst_auto_utils_host_id_context_pop(const char *const *unused)
{
nmtst_utils_host_id_pop();
}
#define _NMTST_UTILS_HOST_ID_CONTEXT(uniq, host_id) \
_nm_unused nm_auto(_nmtst_auto_utils_host_id_context_pop) \
const char *const NM_UNIQ_T(_host_id_context_, uniq) = ({ \
const gint64 _timestamp_ns = 1631000672; \
\
nmtst_utils_host_id_push((const guint8 *) "" host_id "", \
NM_STRLEN(host_id), \
TRUE, \
&_timestamp_ns); \
"" host_id ""; \
})
#define NMTST_UTILS_HOST_ID_CONTEXT(host_id) _NMTST_UTILS_HOST_ID_CONTEXT(NM_UNIQ, host_id)
/*****************************************************************************/
int nm_utils_arp_type_detect_from_hwaddrlen(gsize hwaddr_len);
@ -271,6 +298,8 @@ typedef enum {
NM_UTILS_STABLE_TYPE_RANDOM = 3,
} NMUtilsStableType;
#define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) -1)
NMUtilsStableType nm_utils_stable_id_parse(const char *stable_id,
const char *deviceid,
const char *hwaddr,

View file

@ -1104,6 +1104,14 @@ _l3_config_data_add_obj(NMDedupMultiIndex * multi_idx,
obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source;
modified = TRUE;
}
/* OR assume_config_once flag */
if (obj_new->ip_address.a_assume_config_once
&& !obj_old->ip_address.a_assume_config_once) {
obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
obj_new_stackinit.ip_address.a_assume_config_once = TRUE;
modified = TRUE;
}
break;
case NMP_OBJECT_TYPE_IP4_ROUTE:
case NMP_OBJECT_TYPE_IP6_ROUTE:
@ -1113,6 +1121,14 @@ _l3_config_data_add_obj(NMDedupMultiIndex * multi_idx,
obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source;
modified = TRUE;
}
/* OR assume_config_once flag */
if (obj_new->ip_route.r_assume_config_once
&& !obj_old->ip_route.r_assume_config_once) {
obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
obj_new_stackinit.ip_route.r_assume_config_once = TRUE;
modified = TRUE;
}
break;
default:
nm_assert_not_reached();
@ -2696,7 +2712,7 @@ nm_l3_config_data_merge(NML3ConfigData * self,
const guint32 * default_route_table_x /* length 2, for IS_IPv4 */,
const guint32 * default_route_metric_x /* length 2, for IS_IPv4 */,
const guint32 * default_route_penalty_x /* length 2, for IS_IPv4 */,
NML3ConfigMergeHookAddObj hook_add_addr,
NML3ConfigMergeHookAddObj hook_add_obj,
gpointer hook_user_data)
{
static const guint32 x_default_route_table_x[2] = {RT_TABLE_MAIN, RT_TABLE_MAIN};
@ -2733,36 +2749,67 @@ nm_l3_config_data_merge(NML3ConfigData * self,
src,
&obj,
NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) {
NMPlatformIPXAddress addr_stack;
const NMPlatformIPAddress *addr = NULL;
NMTernary ip4acd_not_ready = NM_TERNARY_DEFAULT;
const NMPlatformIPAddress *a_src = NMP_OBJECT_CAST_IP_ADDRESS(obj);
NMPlatformIPXAddress a;
NML3ConfigMergeHookResult hook_result = {
.ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT,
.assume_config_once = NM_OPTION_BOOL_DEFAULT,
};
if (hook_add_addr && !hook_add_addr(src, obj, &ip4acd_not_ready, hook_user_data))
#define _ensure_a() \
G_STMT_START \
{ \
if (a_src != &a.ax) { \
a_src = &a.ax; \
if (IS_IPv4) \
a.a4 = *NMP_OBJECT_CAST_IP4_ADDRESS(obj); \
else \
a.a6 = *NMP_OBJECT_CAST_IP6_ADDRESS(obj); \
} \
} \
G_STMT_END
nm_assert(a_src->ifindex == self->ifindex);
if (hook_add_obj && !hook_add_obj(src, obj, &hook_result, hook_user_data))
continue;
if (IS_IPv4 && ip4acd_not_ready != NM_TERNARY_DEFAULT
&& (!!ip4acd_not_ready) != NMP_OBJECT_CAST_IP4_ADDRESS(obj)->ip4acd_not_ready) {
addr_stack.a4 = *NMP_OBJECT_CAST_IP4_ADDRESS(obj);
addr_stack.a4.ip4acd_not_ready = (!!ip4acd_not_ready);
addr = &addr_stack.ax;
} else
nm_assert(IS_IPv4 || ip4acd_not_ready == NM_TERNARY_DEFAULT);
nm_assert(IS_IPv4 || hook_result.ip4acd_not_ready == NM_OPTION_BOOL_DEFAULT);
if (hook_result.ip4acd_not_ready != NM_OPTION_BOOL_DEFAULT && IS_IPv4
&& (!!hook_result.ip4acd_not_ready)
!= ((const NMPlatformIP4Address *) a_src)->a_acd_not_ready) {
_ensure_a();
a.a4.a_acd_not_ready = (!!hook_result.ip4acd_not_ready);
}
if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT
&& (!!hook_result.assume_config_once) != a_src->a_assume_config_once) {
_ensure_a();
a.ax.a_assume_config_once = (!!hook_result.assume_config_once);
}
nm_l3_config_data_add_address_full(self,
addr_family,
addr ? NULL : obj,
addr,
a_src == &a.ax ? NULL : obj,
a_src == &a.ax ? a_src : NULL,
NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
NULL);
}
#undef _ensure_a
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES)) {
nm_l3_config_data_iter_obj_for_each (&iter,
src,
&obj,
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) {
const NMPlatformIPRoute *r_src = NMP_OBJECT_CAST_IP_ROUTE(obj);
NMPlatformIPXRoute r;
const NMPlatformIPRoute * r_src = NMP_OBJECT_CAST_IP_ROUTE(obj);
NMPlatformIPXRoute r;
NML3ConfigMergeHookResult hook_result = {
.ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT,
.assume_config_once = NM_OPTION_BOOL_DEFAULT,
};
#define _ensure_r() \
G_STMT_START \
@ -2773,11 +2820,23 @@ nm_l3_config_data_merge(NML3ConfigData * self,
r.r4 = *NMP_OBJECT_CAST_IP4_ROUTE(obj); \
else \
r.r6 = *NMP_OBJECT_CAST_IP6_ROUTE(obj); \
r.rx.ifindex = self->ifindex; \
} \
} \
G_STMT_END
nm_assert(r_src->ifindex == self->ifindex);
if (hook_add_obj && !hook_add_obj(src, obj, &hook_result, hook_user_data))
continue;
nm_assert(hook_result.ip4acd_not_ready == NM_OPTION_BOOL_DEFAULT);
if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT
&& (!!hook_result.assume_config_once) != r_src->r_assume_config_once) {
_ensure_r();
r.rx.r_assume_config_once = (!!hook_result.assume_config_once);
}
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) {
if (r_src->table_any) {
_ensure_r();

View file

@ -52,15 +52,6 @@ typedef enum {
/**
* NML3ConfigMergeFlags:
* @NM_L3_CONFIG_MERGE_FLAGS_NONE: no flags set
* @NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD: if this merge flag is set,
* the the NML3ConfigData doesn't get merged and it's information won't be
* synced. The only purpose is to run ACD on its IPv4 addresses, but
* regardless whether ACD succeeds/fails, the IP addresses won't be configured.
* The point is to run ACD first (without configuring it), and only
* commit the settings if requested. That can either happen by
* nm_l3cfg_add_config() the same NML3Cfg again (with a different
* tag), or by calling nm_l3cfg_add_config() again with this flag
* cleared (and the same tag).
* @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
@ -71,11 +62,10 @@ typedef enum {
*/
typedef enum _nm_packed {
NM_L3_CONFIG_MERGE_FLAGS_NONE = 0,
NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD = (1LL << 0),
NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES = (1LL << 1),
NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES = (1LL << 2),
NM_L3_CONFIG_MERGE_FLAGS_NO_DNS = (1LL << 3),
NM_L3_CONFIG_MERGE_FLAGS_CLONE = (1LL << 4),
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_CLONE = (1LL << 3),
} NML3ConfigMergeFlags;
/*****************************************************************************/
@ -145,10 +135,15 @@ NML3ConfigData *nm_l3_config_data_new_from_platform(NMDedupMultiIndex * mu
NMPlatform * platform,
NMSettingIP6ConfigPrivacy ipv6_privacy_rfc4941);
typedef gboolean (*NML3ConfigMergeHookAddObj)(const NML3ConfigData *l3cd,
const NMPObject * obj,
NMTernary * out_ip4acd_not_ready,
gpointer user_data);
typedef struct {
NMOptionBool ip4acd_not_ready;
NMOptionBool assume_config_once;
} NML3ConfigMergeHookResult;
typedef gboolean (*NML3ConfigMergeHookAddObj)(const NML3ConfigData * l3cd,
const NMPObject * obj,
NML3ConfigMergeHookResult *result,
gpointer user_data);
void nm_l3_config_data_merge(NML3ConfigData * self,
const NML3ConfigData *src,
@ -156,7 +151,7 @@ void nm_l3_config_data_merge(NML3ConfigData * self,
const guint32 *default_route_table_x /* length 2, for IS_IPv4 */,
const guint32 *default_route_metric_x /* length 2, for IS_IPv4 */,
const guint32 *default_route_penalty_x /* length 2, for IS_IPv4 */,
NML3ConfigMergeHookAddObj hook_add_addr,
NML3ConfigMergeHookAddObj hook_add_obj,
gpointer hook_user_data);
GPtrArray *nm_l3_config_data_get_blacklisted_ip4_routes(const NML3ConfigData *self,

View file

@ -593,7 +593,8 @@ _l3cd_config_add(NML3IPv4LL *self)
0,
NM_L3_ACD_DEFEND_TYPE_ONCE,
self->reg_timeout_msec,
NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD))
NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD,
NM_L3_CONFIG_MERGE_FLAGS_NONE))
nm_assert_not_reached();
self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg,

713
src/core/nm-l3-ipv6ll.c Normal file
View file

@ -0,0 +1,713 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "src/core/nm-default-daemon.h"
#include "nm-l3-ipv6ll.h"
#include <linux/if_addr.h>
#include "nm-core-utils.h"
/*****************************************************************************/
/* FIXME(next): ensure that NML3IPv6LL generates the same stable privacy addresses
* as previous implementation. */
/*****************************************************************************/
NM_UTILS_LOOKUP_STR_DEFINE(nm_l3_ipv6ll_state_to_string,
NML3IPv6LLState,
NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("???"),
NM_UTILS_LOOKUP_STR_ITEM(NM_L3_IPV6LL_STATE_NONE, "none"),
NM_UTILS_LOOKUP_STR_ITEM(NM_L3_IPV6LL_STATE_STARTING, "starting"),
NM_UTILS_LOOKUP_STR_ITEM(NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS,
"dad-in-progress"),
NM_UTILS_LOOKUP_STR_ITEM(NM_L3_IPV6LL_STATE_READY, "ready"),
NM_UTILS_LOOKUP_STR_ITEM(NM_L3_IPV6LL_STATE_DAD_FAILED, "dad-failed"), );
/*****************************************************************************/
struct _NML3IPv6LL {
NML3Cfg * l3cfg;
NML3CfgCommitTypeHandle *l3cfg_commit_handle;
NML3IPv6LLNotifyFcn notify_fcn;
gpointer user_data;
GSource * starting_on_idle_source;
GSource * wait_for_addr_source;
GSource * emit_changed_idle_source;
gulong l3cfg_signal_notify_id;
NML3IPv6LLState state;
/* if we have cur_lladdr set, then this might cache the last
* matching NMPObject from the platform cache. This only serves
* for optimizing the lookup to the platform cache. */
const NMPlatformIP6Address *cur_lladdr_obj;
struct in6_addr cur_lladdr;
/* if we have cur_lladdr and _state_has_lladdr() indicates that
* the LL address is suitable, this is a NML3ConfigData instance
* with the configuration. */
const NML3ConfigData *l3cd;
/* "assume" means that we first look whether there is any suitable
* IPv6 address on the device, and in that case, try to use that
* instead of generating a new one. Otherwise, we always try to
* generate a new LL address. */
bool assume : 1;
struct {
NMUtilsStableType stable_type;
guint32 dad_counter;
struct {
const char *ifname;
const char *network_id;
} stable_privacy;
struct {
NMUtilsIPv6IfaceId iid;
} token;
} addrgen;
};
/*****************************************************************************/
#define _NMLOG_DOMAIN LOGD_IP6
#define _NMLOG_PREFIX_NAME "ipv6ll"
#define _NMLOG(level, ...) \
G_STMT_START \
{ \
nm_log((level), \
(_NMLOG_DOMAIN), \
NULL, \
NULL, \
_NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \
",ifindex=%d]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
NM_HASH_OBFUSCATE_PTR(self), \
nm_l3cfg_get_ifindex((self)->l3cfg) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
G_STMT_END
/*****************************************************************************/
#define L3CD_TAG(self) (&(self)->notify_fcn)
/*****************************************************************************/
#define _ASSERT(self) \
G_STMT_START \
{ \
NML3IPv6LL *const _self = (self); \
\
nm_assert(NM_IS_L3_IPV6LL(_self)); \
} \
G_STMT_END
/*****************************************************************************/
static void _check(NML3IPv6LL *self);
/*****************************************************************************/
NML3Cfg *
nm_l3_ipv6ll_get_l3cfg(NML3IPv6LL *self)
{
nm_assert(NM_IS_L3_IPV6LL(self));
return self->l3cfg;
}
int
nm_l3_ipv6ll_get_ifindex(NML3IPv6LL *self)
{
nm_assert(NM_IS_L3_IPV6LL(self));
return nm_l3cfg_get_ifindex(self->l3cfg);
}
NMPlatform *
nm_l3_ipv6ll_get_platform(NML3IPv6LL *self)
{
nm_assert(NM_IS_L3_IPV6LL(self));
return nm_l3cfg_get_platform(self->l3cfg);
}
/*****************************************************************************/
static gboolean
_state_has_lladdr(NML3IPv6LLState state)
{
return NM_IN_SET(state, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, NM_L3_IPV6LL_STATE_READY);
}
NML3IPv6LLState
nm_l3_ipv6ll_get_state(NML3IPv6LL *self, const struct in6_addr **out_lladdr)
{
nm_assert(NM_IS_L3_IPV6LL(self));
NM_SET_OUT(out_lladdr, _state_has_lladdr(self->state) ? &self->cur_lladdr : NULL);
return self->state;
}
static const NML3ConfigData *
_l3cd_config_create(int ifindex, const struct in6_addr *lladdr, NMDedupMultiIndex *multi_idx)
{
NML3ConfigData *l3cd;
nm_assert(ifindex > 0);
nm_assert(lladdr);
nm_assert(IN6_IS_ADDR_LINKLOCAL(lladdr));
l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_IP6LL);
nm_l3_config_data_add_address_6(
l3cd,
NM_PLATFORM_IP6_ADDRESS_INIT(.address = *lladdr,
.plen = 64,
.addr_source = NM_IP_CONFIG_SOURCE_IP6LL));
nm_l3_config_data_add_route_6(
l3cd,
NM_PLATFORM_IP6_ROUTE_INIT(.network.s6_addr16[0] = htons(0xfe80u),
.plen = 64,
.metric_any = TRUE,
.table_any = TRUE,
.rt_source = NM_IP_CONFIG_SOURCE_IP6LL));
return nm_l3_config_data_seal(l3cd);
}
const NML3ConfigData *
nm_l3_ipv6ll_get_l3cd(NML3IPv6LL *self)
{
nm_assert(NM_IS_L3_IPV6LL(self));
if (!_state_has_lladdr(self->state)) {
nm_assert(!self->l3cd);
return NULL;
}
if (!self->l3cd) {
self->l3cd = _l3cd_config_create(nm_l3_ipv6ll_get_ifindex(self),
&self->cur_lladdr,
nm_l3cfg_get_multi_idx(self->l3cfg));
}
return self->l3cd;
}
/*****************************************************************************/
static gboolean
_emit_changed_on_idle_cb(gpointer user_data)
{
NML3IPv6LL * self = user_data;
const struct in6_addr *lladdr;
NML3IPv6LLState state;
char sbuf[INET6_ADDRSTRLEN];
nm_clear_g_source_inst(&self->emit_changed_idle_source);
state = nm_l3_ipv6ll_get_state(self, &lladdr);
_LOGT("emit changed signal (state=%s%s%s)",
nm_l3_ipv6ll_state_to_string(state),
lladdr ? ", " : "",
lladdr ? _nm_utils_inet6_ntop(lladdr, sbuf) : "");
self->notify_fcn(self, state, lladdr, self->user_data);
return G_SOURCE_CONTINUE;
}
/*****************************************************************************/
static gboolean
_generate_new_address(NML3IPv6LL *self, struct in6_addr *out_lladdr)
{
struct in6_addr lladdr;
memset(&lladdr, 0, sizeof(struct in6_addr));
lladdr.s6_addr16[0] = htons(0xfe80u);
if (self->addrgen.stable_type == NM_UTILS_STABLE_TYPE_NONE) {
if (self->addrgen.dad_counter > 0)
return FALSE;
self->addrgen.dad_counter++;
nm_utils_ipv6_addr_set_interface_identifier(&lladdr, &self->addrgen.token.iid);
} else {
/* RFC7217 says we MUST limit the number of retries, and it SHOULD try
* at least IDGEN_RETRIES times (that is, 3 times).
*
* 3 times seems really low. Instead, let's try 6 times. */
G_STATIC_ASSERT(NM_STABLE_PRIVACY_RFC7217_IDGEN_RETRIES == 3);
if (self->addrgen.dad_counter >= NM_STABLE_PRIVACY_RFC7217_IDGEN_RETRIES + 3)
return FALSE;
nm_utils_ipv6_addr_set_stable_privacy(self->addrgen.stable_type,
&lladdr,
self->addrgen.stable_privacy.ifname,
self->addrgen.stable_privacy.network_id,
self->addrgen.dad_counter++);
}
*out_lladdr = lladdr;
return TRUE;
}
/*****************************************************************************/
static gboolean
_pladdr_is_ll_failed(const NMPlatformIP6Address *addr)
{
nm_assert(addr);
nm_assert(IN6_IS_ADDR_LINKLOCAL(&addr->address));
return NM_FLAGS_ANY(addr->n_ifa_flags, IFA_F_DADFAILED | IFA_F_DEPRECATED);
}
static gboolean
_pladdr_is_ll_tentative(const NMPlatformIP6Address *addr)
{
nm_assert(addr);
nm_assert(IN6_IS_ADDR_LINKLOCAL(&addr->address));
nm_assert(!_pladdr_is_ll_failed(addr));
return NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TENTATIVE)
&& !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_OPTIMISTIC);
}
static const NMPlatformIP6Address *
_pladdr_find_ll(NML3IPv6LL *self, gboolean *out_cur_addr_failed)
{
NMDedupMultiIter iter;
NMPLookup lookup;
const NMPlatformIP6Address *pladdr1 = NULL;
const NMPObject * obj;
const NMPlatformIP6Address *pladdr_ready = NULL;
const NMPlatformIP6Address *pladdr_tentative = NULL;
gboolean cur_addr_check = TRUE;
gboolean cur_addr_failed = FALSE;
gboolean pladdr1_looked_up = FALSE;
nm_assert(!self->cur_lladdr_obj
|| IN6_ARE_ADDR_EQUAL(&self->cur_lladdr, &self->cur_lladdr_obj->address));
*out_cur_addr_failed = FALSE;
if (self->state == NM_L3_IPV6LL_STATE_READY && self->cur_lladdr_obj) {
nm_assert(!_pladdr_is_ll_tentative(self->cur_lladdr_obj));
pladdr1 = NMP_OBJECT_CAST_IP6_ADDRESS(
nm_platform_lookup_obj(nm_l3_ipv6ll_get_platform(self),
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
NMP_OBJECT_UP_CAST(self->cur_lladdr_obj)));
if (self->cur_lladdr_obj == pladdr1) {
/* Fast-path. We are ready and the cur_lladdr_obj is still in the cache. We
* got the result with a dictionary lookup without need to iterate over
* all addresses. */
return self->cur_lladdr_obj;
}
pladdr1_looked_up = TRUE;
}
if (!self->assume) {
/* We don't accept any suitable LL address, only he one we are waiting for.
* Let's do a dictionary lookup. */
if (IN6_IS_ADDR_LINKLOCAL(&self->cur_lladdr)) {
if (!pladdr1_looked_up) {
NMPObject needle;
nmp_object_stackinit_id_ip6_address(&needle,
nm_l3_ipv6ll_get_ifindex(self),
&self->cur_lladdr);
pladdr1 = NMP_OBJECT_CAST_IP6_ADDRESS(
nm_platform_lookup_obj(nm_l3_ipv6ll_get_platform(self),
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
&needle));
}
if (pladdr1) {
if (!_pladdr_is_ll_failed(pladdr1))
return pladdr1;
*out_cur_addr_failed = TRUE;
}
} else
nm_assert(!pladdr1_looked_up);
return NULL;
}
if (!NM_IN_SET(self->state, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, NM_L3_IPV6LL_STATE_READY))
cur_addr_check = FALSE;
nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, nm_l3_ipv6ll_get_ifindex(self));
nm_platform_iter_obj_for_each (&iter, nm_l3_ipv6ll_get_platform(self), &lookup, &obj) {
const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj);
if (!IN6_IS_ADDR_LINKLOCAL(&pladdr->address))
continue;
if (_pladdr_is_ll_failed(pladdr)) {
if (cur_addr_check && IN6_ARE_ADDR_EQUAL(&self->cur_lladdr, &pladdr->address)) {
/* "pladdr" is the address we are currently doing DAD for. But it failed.
* We need to recognize and report to the caller, to stop waiting for this
* address. */
cur_addr_failed = TRUE;
cur_addr_check = FALSE;
}
continue;
}
if (_pladdr_is_ll_tentative(pladdr)) {
if (!pladdr_tentative)
pladdr_tentative = pladdr;
else if (pladdr == self->cur_lladdr_obj)
pladdr_tentative = pladdr;
else if (IN6_ARE_ADDR_EQUAL(&self->cur_lladdr, &pladdr->address))
pladdr_tentative = pladdr;
continue;
}
if (pladdr == self->cur_lladdr_obj) {
/* it doesn't get any better. We have our best address. */
return pladdr;
}
if (!pladdr_ready)
pladdr_ready = pladdr;
else if (IN6_ARE_ADDR_EQUAL(&self->cur_lladdr, &pladdr->address))
pladdr_ready = pladdr;
}
*out_cur_addr_failed = cur_addr_failed;
return pladdr_ready ?: pladdr_tentative;
}
/*****************************************************************************/
static void
_lladdr_handle_changed(NML3IPv6LL *self)
{
const NML3ConfigData *l3cd;
gboolean changed = FALSE;
/* We register the l3cd with l3cfg to start DAD. That is different from
* NML3IPv4LL, where we use NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD. The difference
* is that for IPv6 we let kernel do DAD, so we need to actually configure the
* address. For IPv4, we can run ACD without configuring anything in kernel,
* and let the user decide how to proceed.
*
* Also in this case, we use the most graceful commit-type (NM_L3_CFG_COMMIT_TYPE_ASSUME),
* but for that to work, we also need NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE flag. */
l3cd = nm_l3_ipv6ll_get_l3cd(self);
if (l3cd) {
if (nm_l3cfg_add_config(self->l3cfg,
L3CD_TAG(self),
TRUE,
l3cd,
NM_L3CFG_CONFIG_PRIORITY_IPV6LL,
0,
0,
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4,
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
0,
0,
NM_L3_ACD_DEFEND_TYPE_ALWAYS,
0,
NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE,
NM_L3_CONFIG_MERGE_FLAGS_NONE))
changed = TRUE;
} else {
if (nm_l3cfg_remove_config_all(self->l3cfg, L3CD_TAG(self)))
changed = TRUE;
}
self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg,
l3cd ? NM_L3_CFG_COMMIT_TYPE_ASSUME
: NM_L3_CFG_COMMIT_TYPE_NONE,
self->l3cfg_commit_handle);
if (changed)
nm_l3cfg_commit_on_idle_schedule(self->l3cfg);
if (!self->emit_changed_idle_source) {
_LOGT("schedule changed signal on idle");
self->emit_changed_idle_source = nm_g_idle_add_source(_emit_changed_on_idle_cb, self);
}
}
/*****************************************************************************/
static gboolean
_set_cur_lladdr(NML3IPv6LL *self, NML3IPv6LLState state, const struct in6_addr *lladdr)
{
gboolean changed = FALSE;
if (lladdr) {
nm_assert(IN6_IS_ADDR_LINKLOCAL(lladdr));
if (!IN6_ARE_ADDR_EQUAL(&self->cur_lladdr, lladdr)) {
self->cur_lladdr = *lladdr;
nm_clear_l3cd(&self->l3cd);
changed = TRUE;
}
} else {
if (!nm_ip_addr_is_null(AF_INET6, &self->cur_lladdr)) {
nm_clear_l3cd(&self->l3cd);
self->cur_lladdr = nm_ip_addr_zero.addr6;
changed = TRUE;
}
nm_assert(!self->l3cd);
nm_assert(!_state_has_lladdr(state));
}
if (self->state != state) {
if (!_state_has_lladdr(state))
nm_clear_l3cd(&self->l3cd);
self->state = state;
changed = TRUE;
}
return changed;
}
static gboolean
_set_cur_lladdr_obj(NML3IPv6LL *self, NML3IPv6LLState state, const NMPlatformIP6Address *lladdr_obj)
{
nm_assert(lladdr_obj);
nm_assert(_state_has_lladdr(state));
nmp_object_ref_set_up_cast(&self->cur_lladdr_obj, lladdr_obj);
return _set_cur_lladdr(self, state, &lladdr_obj->address);
}
static gboolean
_set_cur_lladdr_bin(NML3IPv6LL *self, NML3IPv6LLState state, const struct in6_addr *lladdr)
{
nmp_object_ref_set_up_cast(&self->cur_lladdr_obj, NULL);
return _set_cur_lladdr(self, state, lladdr);
}
static gboolean
_wait_for_addr_timeout_cb(gpointer user_data)
{
NML3IPv6LL *self = user_data;
nm_clear_g_source_inst(&self->wait_for_addr_source);
nm_assert(
NM_IN_SET(self->state, NM_L3_IPV6LL_STATE_DAD_FAILED, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS));
_check(self);
return G_SOURCE_CONTINUE;
}
static void
_check(NML3IPv6LL *self)
{
const NMPlatformIP6Address *pladdr;
char sbuf[INET6_ADDRSTRLEN];
gboolean cur_addr_failed;
struct in6_addr lladdr;
pladdr = _pladdr_find_ll(self, &cur_addr_failed);
if (pladdr) {
nm_clear_g_source_inst(&self->wait_for_addr_source);
if (_pladdr_is_ll_tentative(pladdr)) {
if (_set_cur_lladdr_obj(self, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, pladdr)) {
_LOGT("changed: waiting for address %s to complete DAD",
_nm_utils_inet6_ntop(&self->cur_lladdr, sbuf));
_lladdr_handle_changed(self);
}
return;
}
if (_set_cur_lladdr_obj(self, NM_L3_IPV6LL_STATE_READY, pladdr)) {
_LOGT("changed: address %s is ready", _nm_utils_inet6_ntop(&self->cur_lladdr, sbuf));
_lladdr_handle_changed(self);
}
return;
}
if (self->cur_lladdr_obj || cur_addr_failed) {
/* we were doing DAD, but the address is no longer a suitable candidate.
* Prematurely abort DAD to generate a new address below. */
nm_assert(
NM_IN_SET(self->state, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, NM_L3_IPV6LL_STATE_READY));
if (self->state == NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS)
_LOGT("changed: address %s did not complete DAD",
_nm_utils_inet6_ntop(&self->cur_lladdr, sbuf));
else {
_LOGT("changed: address %s is gone", _nm_utils_inet6_ntop(&self->cur_lladdr, sbuf));
}
/* reset the state here, so that we are sure that the following
* _set_cur_lladdr_bin() calls (below) will notice the change
* and trigger a _lladdr_handle_changed(). */
_set_cur_lladdr_bin(self, NM_L3_IPV6LL_STATE_STARTING, NULL);
nm_clear_g_source_inst(&self->wait_for_addr_source);
} else if (self->wait_for_addr_source) {
/* we are waiting. Nothing to do for now. */
return;
}
if (!_generate_new_address(self, &lladdr)) {
/* our DAD counter expired. We reset it, and start a timer to retry
* and recover. */
self->addrgen.dad_counter = 0;
self->wait_for_addr_source =
nm_g_timeout_add_source(10000, _wait_for_addr_timeout_cb, self);
if (_set_cur_lladdr_bin(self, NM_L3_IPV6LL_STATE_DAD_FAILED, NULL)) {
_LOGW("changed: no IPv6 link local address to retry after Duplicate Address Detection "
"failures (back off)");
_lladdr_handle_changed(self);
}
return;
}
/* we give NML3Cfg 2 seconds to configure the address on the interface. We
* thus very soon expect to see this address configured (and kernel started DAD).
* If that does not happen within timeout, we assume that this address failed DAD. */
self->wait_for_addr_source = nm_g_timeout_add_source(2000, _wait_for_addr_timeout_cb, self);
if (_set_cur_lladdr_bin(self, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS, &lladdr)) {
_LOGT("changed: starting DAD for address %s",
_nm_utils_inet6_ntop(&self->cur_lladdr, sbuf));
_lladdr_handle_changed(self);
}
return;
}
/*****************************************************************************/
static void
_l3cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NML3IPv6LL *self)
{
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE) {
if (NM_FLAGS_ANY(notify_data->platform_change_on_idle.obj_type_flags,
nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP6_ADDRESS)))
_check(self);
return;
}
}
/*****************************************************************************/
static gboolean
_starting_on_idle_cb(gpointer user_data)
{
NML3IPv6LL *self = user_data;
nm_clear_g_source_inst(&self->starting_on_idle_source);
self->l3cfg_signal_notify_id =
g_signal_connect(self->l3cfg, NM_L3CFG_SIGNAL_NOTIFY, G_CALLBACK(_l3cfg_notify_cb), self);
_check(self);
return G_SOURCE_CONTINUE;
}
/*****************************************************************************/
NML3IPv6LL *
_nm_l3_ipv6ll_new(NML3Cfg * l3cfg,
gboolean assume,
NMUtilsStableType stable_type,
const char * ifname,
const char * network_id,
const NMUtilsIPv6IfaceId *token_iid,
NML3IPv6LLNotifyFcn notify_fcn,
gpointer user_data)
{
NML3IPv6LL *self;
g_return_val_if_fail(NM_IS_L3CFG(l3cfg), NULL);
g_return_val_if_fail(notify_fcn, NULL);
g_return_val_if_fail(
(stable_type == NM_UTILS_STABLE_TYPE_NONE && !ifname && !network_id && token_iid)
|| (stable_type != NM_UTILS_STABLE_TYPE_NONE && ifname && network_id && !token_iid),
NULL);
self = g_slice_new(NML3IPv6LL);
*self = (NML3IPv6LL){
.l3cfg = g_object_ref(l3cfg),
.notify_fcn = notify_fcn,
.user_data = user_data,
.state = NM_L3_IPV6LL_STATE_STARTING,
.starting_on_idle_source = nm_g_idle_add_source(_starting_on_idle_cb, self),
.l3cfg_signal_notify_id = 0,
.cur_lladdr_obj = NULL,
.cur_lladdr = IN6ADDR_ANY_INIT,
.assume = assume,
.addrgen =
{
.stable_type = stable_type,
.dad_counter = 0,
},
};
if (self->addrgen.stable_type == NM_UTILS_STABLE_TYPE_NONE) {
char sbuf_token[sizeof(self->addrgen.token.iid) * 3];
self->addrgen.token.iid = *token_iid;
_LOGT("created: l3cfg=" NM_HASH_OBFUSCATE_PTR_FMT ", ifindex=%d, token=%s%s",
NM_HASH_OBFUSCATE_PTR(l3cfg),
nm_l3cfg_get_ifindex(l3cfg),
nm_utils_bin2hexstr_full(&self->addrgen.token.iid,
sizeof(self->addrgen.token.iid),
':',
FALSE,
sbuf_token),
self->assume ? ", assume" : "");
} else {
self->addrgen.stable_privacy.ifname = g_strdup(ifname);
self->addrgen.stable_privacy.network_id = g_strdup(network_id);
_LOGT("created: l3cfg=" NM_HASH_OBFUSCATE_PTR_FMT
", ifindex=%d, stable-type=%u, ifname=%s, network_id=%s%s",
NM_HASH_OBFUSCATE_PTR(l3cfg),
nm_l3cfg_get_ifindex(l3cfg),
(unsigned) self->addrgen.stable_type,
self->addrgen.stable_privacy.ifname,
self->addrgen.stable_privacy.network_id,
self->assume ? ", assume" : "");
}
return self;
}
void
nm_l3_ipv6ll_destroy(NML3IPv6LL *self)
{
if (!self)
return;
_ASSERT(self);
_LOGT("finalize");
nm_l3cfg_commit_type_unregister(self->l3cfg, g_steal_pointer(&self->l3cfg_commit_handle));
nm_l3cfg_remove_config_all(self->l3cfg, L3CD_TAG(self));
nm_clear_g_source_inst(&self->starting_on_idle_source);
nm_clear_g_source_inst(&self->wait_for_addr_source);
nm_clear_g_source_inst(&self->emit_changed_idle_source);
nm_clear_g_signal_handler(self->l3cfg, &self->l3cfg_signal_notify_id);
g_clear_object(&self->l3cfg);
nm_clear_l3cd(&self->l3cd);
nm_clear_nmp_object_up_cast(&self->cur_lladdr_obj);
if (self->addrgen.stable_type != NM_UTILS_STABLE_TYPE_NONE) {
g_free((char *) self->addrgen.stable_privacy.ifname);
g_free((char *) self->addrgen.stable_privacy.network_id);
}
nm_g_slice_free(self);
}

106
src/core/nm-l3-ipv6ll.h Normal file
View file

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef __NM_L3_IPV6LL_H__
#define __NM_L3_IPV6LL_H__
#include "nm-l3cfg.h"
#include "nm-core-utils.h"
/*****************************************************************************/
typedef struct _NML3IPv6LL NML3IPv6LL;
typedef enum _nm_packed {
/* NONE is not actually used by NML3IPv6LL. This is a bogus placeholder
* state for external users. */
NM_L3_IPV6LL_STATE_NONE,
NM_L3_IPV6LL_STATE_STARTING,
NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS,
NM_L3_IPV6LL_STATE_READY,
NM_L3_IPV6LL_STATE_DAD_FAILED,
} NML3IPv6LLState;
const char *nm_l3_ipv6ll_state_to_string(NML3IPv6LLState state);
typedef void (*NML3IPv6LLNotifyFcn)(NML3IPv6LL * ipv6ll,
NML3IPv6LLState state,
const struct in6_addr *lladdr,
gpointer user_data);
static inline gboolean
NM_IS_L3_IPV6LL(const NML3IPv6LL *self)
{
nm_assert(!self || (NM_IS_L3CFG(*((NML3Cfg **) self))));
return !!self;
}
NML3IPv6LL *_nm_l3_ipv6ll_new(NML3Cfg * l3cfg,
gboolean assume,
NMUtilsStableType stable_type,
const char * ifname,
const char * network_id,
const NMUtilsIPv6IfaceId *token_iid,
NML3IPv6LLNotifyFcn notify_fcn,
gpointer user_data);
static inline NML3IPv6LL *
nm_l3_ipv6ll_new_stable_privacy(NML3Cfg * l3cfg,
gboolean assume,
NMUtilsStableType stable_type,
const char * ifname,
const char * network_id,
NML3IPv6LLNotifyFcn notify_fcn,
gpointer user_data)
{
nm_assert(stable_type != NM_UTILS_STABLE_TYPE_NONE);
return _nm_l3_ipv6ll_new(l3cfg,
assume,
stable_type,
ifname,
network_id,
NULL,
notify_fcn,
user_data);
}
static inline NML3IPv6LL *
nm_l3_ipv6ll_new_token(NML3Cfg * l3cfg,
gboolean assume,
const NMUtilsIPv6IfaceId *token_iid,
NML3IPv6LLNotifyFcn notify_fcn,
gpointer user_data)
{
return _nm_l3_ipv6ll_new(l3cfg,
assume,
NM_UTILS_STABLE_TYPE_NONE,
NULL,
NULL,
token_iid,
notify_fcn,
user_data);
}
void nm_l3_ipv6ll_destroy(NML3IPv6LL *self);
NM_AUTO_DEFINE_FCN0(NML3IPv6LL *, _nm_auto_destroy_l3ipv6ll, nm_l3_ipv6ll_destroy);
#define nm_auto_destroy_l3ipv6ll nm_auto(_nm_auto_destroy_l3ipv6ll)
/*****************************************************************************/
NML3Cfg *nm_l3_ipv6ll_get_l3cfg(NML3IPv6LL *self);
int nm_l3_ipv6ll_get_ifindex(NML3IPv6LL *self);
NMPlatform *nm_l3_ipv6ll_get_platform(NML3IPv6LL *self);
/*****************************************************************************/
NML3IPv6LLState nm_l3_ipv6ll_get_state(NML3IPv6LL *self, const struct in6_addr **out_lladdr);
const NML3ConfigData *nm_l3_ipv6ll_get_l3cd(NML3IPv6LL *self);
/*****************************************************************************/
#endif /* __NM_L3_IPV6LL_H__ */

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@
#include "nm-l3-config-data.h"
#define NM_L3CFG_CONFIG_PRIORITY_IPV4LL 0
#define NM_L3CFG_CONFIG_PRIORITY_IPV6LL 0
#define NM_ACD_TIMEOUT_RFC5227_MSEC 9000u
#define NM_TYPE_L3CFG (nm_l3cfg_get_type())
@ -28,6 +29,37 @@ typedef enum _nm_packed {
NM_L3_ACD_DEFEND_TYPE_ALWAYS,
} NML3AcdDefendType;
/**
* NML3CfgConfigFlags:
* @NM_L3CFG_CONFIG_FLAGS_NONE: no flags, the default.
* @NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD: if this merge flag is set,
* the the NML3ConfigData doesn't get merged and it's information won't be
* synced. The only purpose is to run ACD on its IPv4 addresses, but
* regardless whether ACD succeeds/fails, the IP addresses won't be configured.
* The point is to run ACD first (without configuring it), and only
* commit the settings if requested. That can either happen by
* nm_l3cfg_add_config() the same NML3Cfg again (with a different
* tag), or by calling nm_l3cfg_add_config() again with this flag
* cleared (and the same tag).
* @NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE: a commit with
* %NM_L3_CFG_COMMIT_TYPE_ASSUME, means to not remove/add
* addresses that are missing/already exist. The assume mode
* is for taking over a device gracefully after restart, so
* it aims to preserve whatever was configured (or not configured).
* With this flag enabled, the first commit in assume mode will still
* add the addresses/routes. This is necessary for example with IPv6LL.
* Also while assuming a device, we want to configure things
* (like an IPv6 address), so we need to bypass the common
* "don't change" behavior. At least once. If the address/route
* is still not (no longer) configured on the subsequent
* commit, it's not getting added again.
*/
typedef enum _nm_packed {
NM_L3CFG_CONFIG_FLAGS_NONE = 0,
NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD = (1LL << 0),
NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE = (1LL << 1),
} NML3CfgConfigFlags;
typedef enum _nm_packed {
NM_L3_ACD_ADDR_STATE_INIT,
NM_L3_ACD_ADDR_STATE_PROBING,
@ -291,6 +323,7 @@ gboolean nm_l3cfg_add_config(NML3Cfg * self,
guint32 default_route_penalty_6,
NML3AcdDefendType acd_defend_type,
guint32 acd_timeout_msec,
NML3CfgConfigFlags config_flags,
NML3ConfigMergeFlags merge_flags);
gboolean nm_l3cfg_remove_config(NML3Cfg *self, gconstpointer tag, const NML3ConfigData *ifcfg);

View file

@ -382,7 +382,7 @@ _nm_settings_connection_set_connection(NMSettingsConnection * self,
NM_SETTING_COMPARE_FLAG_EXACT)) {
connection_old = priv->connection;
priv->connection = g_object_ref(new_connection);
nmtst_connection_assert_unchanging(priv->connection);
nm_assert_connection_unchanging(priv->connection);
_getsettings_cached_clear(priv);
_nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(priv->settings);

View file

@ -1352,7 +1352,7 @@ _connection_changed_track(NMSettings * self,
|| (_nm_connection_verify(connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
nm_assert(!connection || nm_streq0(uuid, nm_connection_get_uuid(connection)));
nmtst_connection_assert_unchanging(connection);
nm_assert_connection_unchanging(connection);
sett_conn_entry =
_sett_conn_entries_get(self, uuid) ?: _sett_conn_entries_create_and_add(self, uuid);

View file

@ -112,7 +112,7 @@ nms_ifcfg_rh_storage_new_connection(NMSIfcfgRHPlugin * plugin,
nm_assert(NM_IS_CONNECTION(connection_take));
nm_assert(_nm_connection_verify(connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS);
nmtst_connection_assert_unchanging(connection_take);
nm_assert_connection_unchanging(connection_take);
self = _storage_new(plugin, nm_connection_get_uuid(connection_take), filename);
self->connection = connection_take;

View file

@ -323,7 +323,7 @@ load_eni_ifaces(NMSIfupdownPlugin *self)
NM_PRINT_FMT_QUOTED(local, " (", local->message, ")", ""));
sd = NULL;
} else {
nmtst_connection_assert_unchanging(connection);
nm_assert_connection_unchanging(connection);
uuid = nm_connection_get_uuid(connection);
if (!storage)

View file

@ -168,7 +168,7 @@ nms_keyfile_storage_new_connection(NMSKeyfilePlugin * plugin,
nm_assert(filename && filename[0] == '/');
nm_assert(storage_type >= NMS_KEYFILE_STORAGE_TYPE_RUN
&& storage_type <= _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST);
nmtst_connection_assert_unchanging(connection_take);
nm_assert_connection_unchanging(connection_take);
self = _storage_new(plugin,
nm_connection_get_uuid(connection_take),

View file

@ -327,7 +327,7 @@ test_nm_ip6_config_addresses_sort(void)
0,
0,
0,
IFA_F_TEMPORARY);
IFA_F_SECONDARY);
ADDR_ADD("2607:f0d0:1002:51::8",
NULL,
64,
@ -336,7 +336,7 @@ test_nm_ip6_config_addresses_sort(void)
0,
0,
0,
IFA_F_TEMPORARY);
IFA_F_SECONDARY);
ADDR_ADD("2607:f0d0:1002:51::0",
NULL,
64,
@ -345,7 +345,7 @@ test_nm_ip6_config_addresses_sort(void)
0,
0,
0,
IFA_F_TEMPORARY);
IFA_F_SECONDARY);
ADDR_ADD("fec0::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
@ -374,7 +374,7 @@ test_nm_ip6_config_addresses_sort(void)
0,
0,
0,
IFA_F_TEMPORARY);
IFA_F_SECONDARY);
ADDR_ADD("2607:f0d0:1002:51::4", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0);
ADDR_ADD("2607:f0d0:1002:51::5", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0);
ADDR_ADD("2607:f0d0:1002:51::8",
@ -385,7 +385,7 @@ test_nm_ip6_config_addresses_sort(void)
0,
0,
0,
IFA_F_TEMPORARY);
IFA_F_SECONDARY);
ADDR_ADD("2607:f0d0:1002:51::0",
NULL,
64,
@ -394,7 +394,7 @@ test_nm_ip6_config_addresses_sort(void)
0,
0,
0,
IFA_F_TEMPORARY);
IFA_F_SECONDARY);
ADDR_ADD("2607:f0d0:1002:51::6",
NULL,
64,

View file

@ -2,8 +2,11 @@
#include "src/core/nm-default-daemon.h"
#include <linux/if_addr.h>
#include "nm-l3cfg.h"
#include "nm-l3-ipv4ll.h"
#include "nm-l3-ipv6ll.h"
#include "nm-netns.h"
#include "libnm-platform/nm-platform.h"
@ -434,6 +437,7 @@ test_l3cfg(gconstpointer test_data)
0,
tdata->acd_defend_type_a,
tdata->acd_timeout_msec_a,
NM_L3CFG_CONFIG_FLAGS_NONE,
NM_L3_CONFIG_MERGE_FLAGS_NONE);
}
@ -595,6 +599,7 @@ _test_l3_ipv4ll_signal_notify(NML3Cfg * l3cfg,
0,
NM_L3_ACD_DEFEND_TYPE_ONCE,
nmtst_get_rand_bool() ? tdata->acd_timeout_msec : 0u,
NM_L3CFG_CONFIG_FLAGS_NONE,
NM_L3_CONFIG_MERGE_FLAGS_NONE))
g_assert_not_reached();
nm_l3cfg_commit_on_idle_schedule(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll));
@ -778,6 +783,276 @@ test_l3_ipv4ll(gconstpointer test_data)
/*****************************************************************************/
#define _LLADDR_TEST1 "fe80::dd5a:8a44:48bc:3ad"
#define _LLADDR_TEST2 "fe80::878b:938e:46f9:4807"
typedef struct {
const TestFixture1 *f;
NML3Cfg * l3cfg0;
NML3IPv6LL * l3ipv6ll;
int step;
int ipv6ll_callback_step;
bool steps_done : 1;
const NMPObject * lladdr0;
} TestL3IPv6LLData;
static const NMPlatformIP6Address *
_test_l3_ipv6ll_find_lladdr(TestL3IPv6LLData *tdata, int ifindex)
{
const NMPlatformIP6Address *found = NULL;
NMDedupMultiIter iter;
const NMPObject * obj;
NMPLookup lookup;
g_assert(tdata);
nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex);
nm_platform_iter_obj_for_each (&iter, tdata->f->platform, &lookup, &obj) {
const NMPlatformIP6Address *a = NMP_OBJECT_CAST_IP6_ADDRESS(obj);
if (!IN6_IS_ADDR_LINKLOCAL(&a->address))
continue;
if (!found)
found = a;
else
g_assert_not_reached();
}
return found;
}
static const NMPObject *
_test_l3_ipv6ll_find_lladdr_wait(TestL3IPv6LLData *tdata, int ifindex)
{
const NMPObject *obj = NULL;
nmtst_main_context_iterate_until_assert(NULL, 3000, ({
const NMPlatformIP6Address *a;
a = _test_l3_ipv6ll_find_lladdr(tdata, ifindex);
if (a
&& !NM_FLAGS_HAS(a->n_ifa_flags,
IFA_F_TENTATIVE))
obj = NMP_OBJECT_UP_CAST(a);
obj;
}));
return obj;
}
static const NMPlatformIP6Address *
_test_l3_ipv6ll_find_inet6(TestL3IPv6LLData *tdata, const struct in6_addr *addr)
{
const NMPlatformIP6Address *a;
a = nmtstp_platform_ip6_address_find(nm_l3cfg_get_platform(tdata->l3cfg0),
nmtst_get_rand_bool() ? 0 : tdata->f->ifindex0,
addr);
if (a) {
g_assert_cmpint(a->ifindex, ==, tdata->f->ifindex0);
g_assert_cmpmem(addr, sizeof(*addr), &a->address, sizeof(a->address));
}
g_assert(a
== nm_platform_ip6_address_get(nm_l3cfg_get_platform(tdata->l3cfg0),
tdata->f->ifindex0,
addr));
return a;
}
static void
_test_l3_ipv6ll_signal_notify(NML3Cfg * l3cfg,
const NML3ConfigNotifyData *notify_data,
TestL3IPv6LLData * tdata)
{
g_assert_cmpint(tdata->step, >=, 1);
g_assert_cmpint(tdata->step, <=, 2);
}
static void
_test_l3_ipv6ll_callback_changed(NML3IPv6LL * ipv6ll,
NML3IPv6LLState state,
const struct in6_addr *lladdr,
gpointer user_data)
{
TestL3IPv6LLData * tdata = user_data;
int step = tdata->ipv6ll_callback_step++;
const NMPlatformIP6Address *a1;
g_assert_cmpint(tdata->step, ==, 1);
g_assert(!tdata->steps_done);
switch (step) {
case 0:
if (NM_IN_SET(tdata->f->test_idx, 1, 2, 4)) {
g_assert_cmpint(state, ==, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS);
g_assert_cmpstr(nmtst_inet6_to_string(lladdr), ==, _LLADDR_TEST1);
} else if (NM_IN_SET(tdata->f->test_idx, 3)) {
g_assert_cmpint(state, ==, NM_L3_IPV6LL_STATE_READY);
g_assert(
IN6_ARE_ADDR_EQUAL(lladdr, &NMP_OBJECT_CAST_IP6_ADDRESS(tdata->lladdr0)->address));
tdata->steps_done = TRUE;
} else
g_assert_not_reached();
break;
case 1:
if (NM_IN_SET(tdata->f->test_idx, 1, 2)) {
g_assert_cmpint(state, ==, NM_L3_IPV6LL_STATE_READY);
g_assert_cmpstr(nmtst_inet6_to_string(lladdr), ==, _LLADDR_TEST1);
a1 = _test_l3_ipv6ll_find_inet6(tdata, lladdr);
g_assert(a1);
g_assert(!NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE));
tdata->steps_done = TRUE;
} else if (NM_IN_SET(tdata->f->test_idx, 4)) {
g_assert_cmpint(state, ==, NM_L3_IPV6LL_STATE_DAD_IN_PROGRESS);
g_assert_cmpstr(nmtst_inet6_to_string(lladdr), ==, _LLADDR_TEST2);
} else
g_assert_not_reached();
break;
case 2:
if (NM_IN_SET(tdata->f->test_idx, 4)) {
g_assert_cmpint(state, ==, NM_L3_IPV6LL_STATE_READY);
g_assert_cmpstr(nmtst_inet6_to_string(lladdr), ==, _LLADDR_TEST2);
a1 = _test_l3_ipv6ll_find_inet6(tdata, lladdr);
g_assert(a1);
g_assert(!NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE));
tdata->steps_done = TRUE;
} else
g_assert_not_reached();
break;
default:
g_assert_not_reached();
}
}
static void
test_l3_ipv6ll(gconstpointer test_data)
{
NMTST_UTILS_HOST_ID_CONTEXT("l3-ipv6ll");
const int TEST_IDX = GPOINTER_TO_INT(test_data);
nm_auto(_test_fixture_1_teardown) TestFixture1 test_fixture = {};
gs_unref_object NML3Cfg *l3cfg0 = NULL;
TestL3IPv6LLData tdata_stack = {
.step = 0,
.steps_done = FALSE,
};
TestL3IPv6LLData *const tdata = &tdata_stack;
char sbuf1[sizeof(_nm_utils_to_string_buffer)];
int r;
_LOGD("test start (/l3-ipv6ll/%d)", TEST_IDX);
if (nmtst_test_quick()) {
gs_free char *msg =
g_strdup_printf("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n",
g_get_prgname() ?: "test-l3-ipv6ll");
g_test_skip(msg);
return;
}
tdata->f = _test_fixture_1_setup(&test_fixture, TEST_IDX);
if (NM_IN_SET(tdata->f->test_idx, 4)) {
_LOGD("add conflicting IPv6LL on other interface...");
r = nm_platform_link_change_flags(tdata->f->platform, tdata->f->ifindex1, IFF_UP, FALSE);
g_assert_cmpint(r, >=, 0);
r = nm_platform_link_set_inet6_addr_gen_mode(tdata->f->platform,
tdata->f->ifindex1,
NM_IN6_ADDR_GEN_MODE_NONE);
g_assert_cmpint(r, >=, 0);
r = nm_platform_link_change_flags(tdata->f->platform, tdata->f->ifindex1, IFF_UP, TRUE);
g_assert_cmpint(r, >=, 0);
nmtstp_ip6_address_add(tdata->f->platform,
-1,
tdata->f->ifindex1,
*nmtst_inet6_from_string(_LLADDR_TEST1),
64,
in6addr_any,
NM_PLATFORM_LIFETIME_PERMANENT,
NM_PLATFORM_LIFETIME_PERMANENT,
0);
_LOGD("wait for IPv6 LL address...");
tdata->lladdr0 =
nmp_object_ref(_test_l3_ipv6ll_find_lladdr_wait(tdata, tdata->f->ifindex1));
} else if (NM_IN_SET(tdata->f->test_idx, 2, 3)) {
_LOGD("wait for IPv6 LL address...");
tdata->lladdr0 =
nmp_object_ref(_test_l3_ipv6ll_find_lladdr_wait(tdata, tdata->f->ifindex0));
}
if (tdata->lladdr0) {
_LOGD("got IPv6 LL address %s",
nmp_object_to_string(tdata->lladdr0,
NMP_OBJECT_TO_STRING_PUBLIC,
sbuf1,
sizeof(sbuf1)));
}
l3cfg0 = _netns_access_l3cfg(tdata->f->netns, tdata->f->ifindex0);
tdata->l3cfg0 = l3cfg0;
g_signal_connect(tdata->l3cfg0,
NM_L3CFG_SIGNAL_NOTIFY,
G_CALLBACK(_test_l3_ipv6ll_signal_notify),
tdata);
tdata->l3ipv6ll = nm_l3_ipv6ll_new_stable_privacy(tdata->l3cfg0,
NM_IN_SET(tdata->f->test_idx, 3),
NM_UTILS_STABLE_TYPE_UUID,
tdata->f->ifname0,
"b6a5b934-c649-43dc-a524-3dfdb74f9419",
_test_l3_ipv6ll_callback_changed,
tdata);
g_assert(nm_l3_ipv6ll_get_l3cfg(tdata->l3ipv6ll) == tdata->l3cfg0);
g_assert_cmpint(nm_l3_ipv6ll_get_ifindex(tdata->l3ipv6ll), ==, tdata->f->ifindex0);
tdata->step = 1;
nmtst_main_context_iterate_until_assert(NULL, 7000, tdata->steps_done);
g_assert_cmpint(tdata->step, ==, 1);
if (NM_IN_SET(tdata->f->test_idx, 3))
g_assert_cmpint(tdata->ipv6ll_callback_step, ==, 1);
else if (NM_IN_SET(tdata->f->test_idx, 4))
g_assert_cmpint(tdata->ipv6ll_callback_step, ==, 3);
else
g_assert_cmpint(tdata->ipv6ll_callback_step, ==, 2);
g_assert(tdata->steps_done);
tdata->step = 2;
nmtst_main_context_iterate_until(NULL, nmtst_get_rand_uint32() % 1000, FALSE);
g_assert_cmpint(tdata->step, ==, 2);
if (NM_IN_SET(tdata->f->test_idx, 3))
g_assert_cmpint(tdata->ipv6ll_callback_step, ==, 1);
else if (NM_IN_SET(tdata->f->test_idx, 4))
g_assert_cmpint(tdata->ipv6ll_callback_step, ==, 3);
else
g_assert_cmpint(tdata->ipv6ll_callback_step, ==, 2);
g_assert(tdata->steps_done);
g_assert(tdata->steps_done);
tdata->step = 0;
tdata->steps_done = FALSE;
g_signal_handlers_disconnect_by_func(tdata->l3cfg0,
G_CALLBACK(_test_l3_ipv6ll_signal_notify),
tdata);
nm_l3_ipv6ll_destroy(tdata->l3ipv6ll);
nm_clear_nmp_object(&tdata->lladdr0);
}
/*****************************************************************************/
NMTstpSetupFunc const _nmtstp_setup_platform_func = nm_linux_platform_setup;
void
@ -795,4 +1070,8 @@ _nmtstp_setup_tests(void)
g_test_add_data_func("/l3cfg/4", GINT_TO_POINTER(4), test_l3cfg);
g_test_add_data_func("/l3-ipv4ll/1", GINT_TO_POINTER(1), test_l3_ipv4ll);
g_test_add_data_func("/l3-ipv4ll/2", GINT_TO_POINTER(2), test_l3_ipv4ll);
g_test_add_data_func("/l3-ipv6ll/1", GINT_TO_POINTER(1), test_l3_ipv6ll);
g_test_add_data_func("/l3-ipv6ll/2", GINT_TO_POINTER(2), test_l3_ipv6ll);
g_test_add_data_func("/l3-ipv6ll/3", GINT_TO_POINTER(3), test_l3_ipv6ll);
g_test_add_data_func("/l3-ipv6ll/4", GINT_TO_POINTER(4), test_l3_ipv6ll);
}

View file

@ -2115,23 +2115,23 @@ _nm_connection_ensure_normalized(NMConnection * connection,
#if NM_MORE_ASSERTS
static void
_nmtst_connection_unchanging_changed_cb(NMConnection *connection, gpointer user_data)
_nm_assert_connection_unchanging_changed_cb(NMConnection *connection, gpointer user_data)
{
nm_assert_not_reached();
}
static void
_nmtst_connection_unchanging_secrets_updated_cb(NMConnection *connection,
const char * setting_name,
gpointer user_data)
_nm_assert_connection_unchanging_secrets_updated_cb(NMConnection *connection,
const char * setting_name,
gpointer user_data)
{
nm_assert_not_reached();
}
const char _nmtst_connection_unchanging_user_data = 0;
const char _nm_assert_connection_unchanging_user_data = 0;
void
nmtst_connection_assert_unchanging(NMConnection *connection)
nm_assert_connection_unchanging(NMConnection *connection)
{
if (!connection)
return;
@ -2144,7 +2144,7 @@ nmtst_connection_assert_unchanging(NMConnection *connection)
0,
NULL,
NULL,
(gpointer) &_nmtst_connection_unchanging_user_data)
(gpointer) &_nm_assert_connection_unchanging_user_data)
!= 0) {
/* avoid connecting the assertion handler multiple times. */
return;
@ -2152,16 +2152,16 @@ nmtst_connection_assert_unchanging(NMConnection *connection)
g_signal_connect(connection,
NM_CONNECTION_CHANGED,
G_CALLBACK(_nmtst_connection_unchanging_changed_cb),
(gpointer) &_nmtst_connection_unchanging_user_data);
G_CALLBACK(_nm_assert_connection_unchanging_changed_cb),
(gpointer) &_nm_assert_connection_unchanging_user_data);
g_signal_connect(connection,
NM_CONNECTION_SECRETS_CLEARED,
G_CALLBACK(_nmtst_connection_unchanging_changed_cb),
(gpointer) &_nmtst_connection_unchanging_user_data);
G_CALLBACK(_nm_assert_connection_unchanging_changed_cb),
(gpointer) &_nm_assert_connection_unchanging_user_data);
g_signal_connect(connection,
NM_CONNECTION_SECRETS_UPDATED,
G_CALLBACK(_nmtst_connection_unchanging_secrets_updated_cb),
(gpointer) &_nmtst_connection_unchanging_user_data);
G_CALLBACK(_nm_assert_connection_unchanging_secrets_updated_cb),
(gpointer) &_nm_assert_connection_unchanging_user_data);
}
#endif
@ -2338,7 +2338,7 @@ nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints)
if (!setting)
continue;
nm_assert(!setting_before || _nmtst_nm_setting_sort(setting_before, setting) < 0);
nm_assert(!setting_before || _nm_setting_sort_for_nm_assert(setting_before, setting) < 0);
nm_assert(!setting_before || _nm_setting_compare_priority(setting_before, setting) <= 0);
setting_before = setting;
@ -2660,7 +2660,7 @@ nm_connection_is_type(NMConnection *connection, const char *type)
}
int
_nmtst_nm_setting_sort(NMSetting *a, NMSetting *b)
_nm_setting_sort_for_nm_assert(NMSetting *a, NMSetting *b)
{
g_assert(NM_IS_SETTING(a));
g_assert(NM_IS_SETTING(b));
@ -2727,7 +2727,7 @@ nm_connection_get_settings(NMConnection *connection, guint *out_length)
NMSetting *setting = priv->settings[nm_meta_setting_types_by_priority[i]];
if (setting) {
nm_assert(j == 0 || _nmtst_nm_setting_sort(arr[j - 1], setting) < 0);
nm_assert(j == 0 || _nm_setting_sort_for_nm_assert(arr[j - 1], setting) < 0);
arr[j++] = setting;
}
}

View file

@ -175,7 +175,7 @@ void _nm_setting_ip_config_private_init(gpointer self, NMSettingIPConfigPrivate
NMSettingPriority _nm_setting_get_base_type_priority(NMSetting *setting);
int _nm_setting_compare_priority(gconstpointer a, gconstpointer b);
int _nmtst_nm_setting_sort(NMSetting *a, NMSetting *b);
int _nm_setting_sort_for_nm_assert(NMSetting *a, NMSetting *b);
/*****************************************************************************/

View file

@ -161,7 +161,7 @@ dispose(GObject *object)
#if NM_MORE_ASSERTS
g_signal_handlers_disconnect_by_data(object,
(gpointer) &_nmtst_connection_unchanging_user_data);
(gpointer) &_nm_assert_connection_unchanging_user_data);
#endif
nm_connection_clear_secrets(connection);

View file

@ -152,7 +152,7 @@ test_nm_meta_setting_types_by_priority(void)
for (j = 0; j < i; j++) {
NMSetting *other = arr->pdata[j];
if (_nmtst_nm_setting_sort(other, setting) >= 0) {
if (_nm_setting_sort_for_nm_assert(other, setting) >= 0) {
g_error("sort order for nm_meta_setting_types_by_priority[%d vs %d] is wrong: %s "
"should be before %s",
j,

View file

@ -272,11 +272,11 @@ gboolean _nm_connection_ensure_normalized(NMConnection * connection,
gboolean _nm_connection_remove_setting(NMConnection *connection, GType setting_type);
#if NM_MORE_ASSERTS
extern const char _nmtst_connection_unchanging_user_data;
void nmtst_connection_assert_unchanging(NMConnection *connection);
extern const char _nm_assert_connection_unchanging_user_data;
void nm_assert_connection_unchanging(NMConnection *connection);
#else
static inline void
nmtst_connection_assert_unchanging(NMConnection *connection)
nm_assert_connection_unchanging(NMConnection *connection)
{}
#endif

View file

@ -18,6 +18,7 @@ typedef enum _nm_packed {
NM_OPTION_BOOL_TRUE = 1,
} NMOptionBool;
#define nm_assert_is_bool(value) nm_assert(NM_IN_SET((value), 0, 1))
#define nm_assert_is_ternary(value) nm_assert(NM_IN_SET((value), -1, 0, 1))
/*****************************************************************************/
@ -2096,6 +2097,28 @@ nm_g_array_unref(GArray *arr)
g_array_unref(arr);
}
#define nm_g_array_first(arr, type) \
({ \
GArray *const _arr = (arr); \
guint _len; \
\
nm_assert(_arr); \
_len = _arr->len; \
nm_assert(_len > 0); \
&g_array_index(arr, type, 0); \
})
#define nm_g_array_last(arr, type) \
({ \
GArray *const _arr = (arr); \
guint _len; \
\
nm_assert(_arr); \
_len = _arr->len; \
nm_assert(_len > 0); \
&g_array_index(arr, type, _len - 1u); \
})
#define nm_g_array_append_new(arr, type) \
({ \
GArray *const _arr = (arr); \

View file

@ -1458,6 +1458,19 @@ next:;
/*****************************************************************************/
/* uses an expression statement to copy and return @arg. The use is for functions
* like nmtst_inet4_from_string(), which return a static (thread-local) variable.
* If you use such a statement twice, the result will be overwritten. The macro
* prevents that. */
#define NMTST_COPY(arg) \
({ \
typeof(arg) _arg = (arg); \
\
_arg; \
})
/*****************************************************************************/
#define __define_nmtst_static(NUM, SIZE) \
static inline const char *nmtst_static_##SIZE##_##NUM(const char *str) \
{ \

View file

@ -3657,7 +3657,7 @@ _addr_array_clean_expired(int addr_family,
}
#endif
if (!NM_IS_IPv4(addr_family) && NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_TEMPORARY)) {
if (!NM_IS_IPv4(addr_family) && NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_SECONDARY)) {
/* temporary addresses are never added explicitly by NetworkManager but
* kernel adds them via mngtempaddr flag.
*
@ -4262,7 +4262,7 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self,
if (!IS_IPv4) {
if (exclude_ipv6_temporary_addrs
&& NM_FLAGS_HAS(NMP_OBJECT_CAST_IP_ADDRESS(obj)->n_ifa_flags, IFA_F_TEMPORARY))
&& NM_FLAGS_HAS(NMP_OBJECT_CAST_IP_ADDRESS(obj)->n_ifa_flags, IFA_F_SECONDARY))
continue;
}
@ -6303,7 +6303,8 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf
"%s" /* label */
" src %s"
"%s" /* external */
"%s" /* ip4acd_not_ready */
"%s" /* a_acd_not_ready */
"%s" /* a_assume_config_once */
"",
s_address,
address->plen,
@ -6322,7 +6323,8 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf
str_label,
nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)),
address->external ? " ext" : "",
address->ip4acd_not_ready ? " ip4acd-not-ready" : "");
address->a_acd_not_ready ? " ip4acd-not-ready" : "",
address->a_assume_config_once ? " assume-config-once" : "");
g_free(str_peer);
return buf;
}
@ -6356,17 +6358,22 @@ NM_UTILS_ENUM2STR_DEFINE(nm_platform_link_inet6_addrgenmode2str,
NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"),
NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_RANDOM, "random"), );
G_STATIC_ASSERT(IFA_F_SECONDARY == IFA_F_TEMPORARY);
NM_UTILS_FLAGS2STR_DEFINE(nm_platform_addr_flags2str,
unsigned,
NM_UTILS_FLAGS2STR(IFA_F_SECONDARY, "secondary"),
NM_UTILS_FLAGS2STR(IFA_F_NODAD, "nodad"),
NM_UTILS_FLAGS2STR(IFA_F_OPTIMISTIC, "optimistic"),
NM_UTILS_FLAGS2STR(IFA_F_DADFAILED, "dadfailed"),
NM_UTILS_FLAGS2STR(IFA_F_HOMEADDRESS, "homeaddress"),
NM_UTILS_FLAGS2STR(IFA_F_DEPRECATED, "deprecated"),
NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"),
NM_UTILS_FLAGS2STR(IFA_F_PERMANENT, "permanent"),
NM_UTILS_FLAGS2STR(IFA_F_MANAGETEMPADDR, "mngtmpaddr"),
NM_UTILS_FLAGS2STR(IFA_F_NOPREFIXROUTE, "noprefixroute"),
NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"), );
NM_UTILS_FLAGS2STR(IFA_F_MCAUTOJOIN, "mcautojoin"),
NM_UTILS_FLAGS2STR(IFA_F_STABLE_PRIVACY, "stable-privacy"), );
NM_UTILS_ENUM2STR_DEFINE(nm_platform_route_scope2str,
int,
@ -6436,7 +6443,10 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf
g_snprintf(
buf,
len,
"%s/%d lft %s pref %s%s%s%s%s src %s%s",
"%s/%d lft %s pref %s%s%s%s%s src %s"
"%s" /* external */
"%s" /* a_assume_config_once */
"",
s_address,
address->plen,
str_lft_p,
@ -6446,7 +6456,8 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf
str_dev,
_to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)),
nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)),
address->external ? " ext" : "");
address->external ? " external" : "",
address->a_assume_config_once ? " assume-config-once" : "");
g_free(str_peer);
return buf;
}
@ -6538,6 +6549,7 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz
"%s" /* initrwnd */
"%s" /* mtu */
"%s" /* is_external */
"%s" /* r_assume_config_once */
"",
nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced),
str_type),
@ -6594,7 +6606,8 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz
route->lock_mtu ? "lock " : "",
route->mtu)
: "",
route->is_external ? " (E)" : "");
route->is_external ? " is-external" : "",
route->r_assume_config_once ? " assume-config-once" : "");
return buf;
}
@ -6665,6 +6678,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz
"%s" /* mtu */
"%s" /* pref */
"%s" /* is_external */
"%s" /* r_assume_config_once */
"",
nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced),
str_type),
@ -6725,7 +6739,8 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz
" pref %s",
nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2)))
: "",
route->is_external ? " (E)" : "");
route->is_external ? " is-external" : "",
route->r_assume_config_once ? " assume-config-once" : "");
return buf;
}
@ -7809,20 +7824,20 @@ nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1,
NM_CMP_DIRECT(_address_pretty_sort_get_prio_6(&a2->address),
_address_pretty_sort_get_prio_6(&a1->address));
ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY);
ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY);
ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY);
ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY);
if (ipv6_privacy1 || ipv6_privacy2) {
gboolean public1 = TRUE;
gboolean public2 = TRUE;
if (ipv6_privacy1) {
if (a1->n_ifa_flags & IFA_F_TEMPORARY)
if (a1->n_ifa_flags & IFA_F_SECONDARY)
public1 = prefer_temp;
else
public1 = !prefer_temp;
}
if (ipv6_privacy2) {
if (a2->n_ifa_flags & IFA_F_TEMPORARY)
if (a2->n_ifa_flags & IFA_F_SECONDARY)
public2 = prefer_temp;
else
public2 = !prefer_temp;
@ -7861,7 +7876,8 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState
NM_HASH_COMBINE_BOOLS(guint8,
obj->external,
obj->use_ip4_broadcast_address,
obj->ip4acd_not_ready));
obj->a_acd_not_ready,
obj->a_assume_config_once));
nm_hash_update_strarr(h, obj->label);
}
@ -7883,7 +7899,8 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Ad
NM_CMP_FIELD(a, b, n_ifa_flags);
NM_CMP_FIELD_STR(a, b, label);
NM_CMP_FIELD_UNSAFE(a, b, external);
NM_CMP_FIELD_UNSAFE(a, b, ip4acd_not_ready);
NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready);
NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once);
return 0;
}
@ -7900,7 +7917,7 @@ nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState
obj->plen,
obj->address,
obj->peer_address,
NM_HASH_COMBINE_BOOLS(guint8, obj->external));
NM_HASH_COMBINE_BOOLS(guint8, obj->external, obj->a_assume_config_once));
}
int
@ -7921,6 +7938,7 @@ nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Ad
NM_CMP_FIELD(a, b, preferred);
NM_CMP_FIELD(a, b, n_ifa_flags);
NM_CMP_FIELD_UNSAFE(a, b, external);
NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once);
return 0;
}
@ -8021,7 +8039,7 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
obj->initrwnd,
obj->mtu,
obj->r_rtm_flags,
NM_HASH_COMBINE_BOOLS(guint8,
NM_HASH_COMBINE_BOOLS(guint16,
obj->metric_any,
obj->table_any,
obj->lock_window,
@ -8029,7 +8047,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
obj->lock_initcwnd,
obj->lock_initrwnd,
obj->lock_mtu,
obj->is_external));
obj->is_external,
obj->r_assume_config_once));
break;
}
}
@ -8119,8 +8138,10 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
NM_CMP_FIELD(a, b, initcwnd);
NM_CMP_FIELD(a, b, initrwnd);
NM_CMP_FIELD(a, b, mtu);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL)
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) {
NM_CMP_FIELD_UNSAFE(a, b, is_external);
NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once);
}
break;
}
return 0;
@ -8205,7 +8226,7 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
obj->rt_source,
obj->mss,
obj->r_rtm_flags,
NM_HASH_COMBINE_BOOLS(guint8,
NM_HASH_COMBINE_BOOLS(guint16,
obj->metric_any,
obj->table_any,
obj->lock_window,
@ -8213,7 +8234,8 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
obj->lock_initcwnd,
obj->lock_initrwnd,
obj->lock_mtu,
obj->is_external),
obj->is_external,
obj->r_assume_config_once),
obj->window,
obj->cwnd,
obj->initcwnd,
@ -8296,8 +8318,10 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref));
else
NM_CMP_FIELD(a, b, rt_pref);
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL)
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) {
NM_CMP_FIELD_UNSAFE(a, b, is_external);
NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once);
}
break;
}
return 0;

View file

@ -325,10 +325,10 @@ typedef enum {
\
bool use_ip4_broadcast_address : 1; \
\
/* Whether the address is ready to be configured. By default, an address is, but this
* flag may indicate that the address is just for tracking purpose only, but the ACD
* state is not yet ready for the address to be configured. */ \
bool ip4acd_not_ready : 1; \
/* Whether the address is should be configured once during assume. This is a meta flag
* that is not honored by NMPlatform (netlink code). Instead, it can be used by the upper
* layers which use NMPlatformIPAddress to track addresses that should be configured. */ \
bool a_assume_config_once : 1; \
;
/**
@ -351,6 +351,11 @@ typedef struct {
struct _NMPlatformIP4Address {
__NMPlatformIPAddress_COMMON;
/* Whether the address is ready to be configured. By default, an address is, but this
* flag may indicate that the address is just for tracking purpose only, but the ACD
* state is not yet ready for the address to be configured. */
bool a_acd_not_ready : 1;
/* The local address IFA_LOCAL. */
in_addr_t address;
@ -473,6 +478,11 @@ typedef union {
* and is not reflected on netlink. */ \
bool is_external : 1; \
\
/* Whether the route is should be configured once during assume. This is a meta flag
* that is not honored by NMPlatform (netlink code). Instead, it can be used by the upper
* layers which use NMPlatformIPRoute to track routes that should be configured. */ \
bool r_assume_config_once : 1; \
\
/* rtnh_flags
*
* Routes with rtm_flags RTM_F_CLONED are hidden by platform and

View file

@ -1082,6 +1082,21 @@ nm_platform_lookup_object_by_addr_family(NMPlatform * platform,
/*****************************************************************************/
static inline gboolean
nmp_object_get_assume_config_once(const NMPObject *obj)
{
switch (NMP_OBJECT_GET_TYPE(obj)) {
case NMP_OBJECT_TYPE_IP4_ADDRESS:
case NMP_OBJECT_TYPE_IP6_ADDRESS:
return NMP_OBJECT_CAST_IP_ADDRESS(obj)->a_assume_config_once;
case NMP_OBJECT_TYPE_IP4_ROUTE:
case NMP_OBJECT_TYPE_IP6_ROUTE:
return NMP_OBJECT_CAST_IP_ROUTE(obj)->r_assume_config_once;
default:
return nm_assert_unreachable_val(FALSE);
}
}
static inline const char *
nmp_object_link_get_ifname(const NMPObject *obj)
{