mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-08 18:50:18 +01:00
l3cfg: merge branch 'th/l3cfg-ipv6ll'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/976
This commit is contained in:
commit
3a6b3e35da
29 changed files with 2117 additions and 464 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
713
src/core/nm-l3-ipv6ll.c
Normal 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
106
src/core/nm-l3-ipv6ll.h
Normal 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__ */
|
||||
1007
src/core/nm-l3cfg.c
1007
src/core/nm-l3cfg.c
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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); \
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
{ \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue