NetworkManager/src/core/nm-l3-ipv6ll.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

722 lines
26 KiB
C
Raw Normal View History

/* 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(l3cfg): 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_DEFUNCT, "defunct"),
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;
guint32 route_table;
/* "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,
self->route_table,
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4,
NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
0,
0,
NM_DNS_PRIORITY_DEFAULT_NORMAL,
NM_DNS_PRIORITY_DEFAULT_NORMAL,
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,
"ipv6ll");
if (changed)
nm_l3cfg_commit_on_idle_schedule(self->l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO);
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,
guint32 route_table,
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,
.route_table = route_table,
.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);
}