NetworkManager/src/core/nm-l3-ipv6ll.c
Thomas Haller 615221a99c format: reformat source tree with clang-format 13.0
We use clang-format for automatic formatting of our source files.
Since clang-format is actively maintained software, the actual
formatting depends on the used version of clang-format. That is
unfortunate and painful, but really unavoidable unless clang-format
would be strictly bug-compatible.

So the version that we must use is from the current Fedora release, which
is also tested by our gitlab-ci. Previously, we were using Fedora 34 with
clang-tools-extra-12.0.1-1.fc34.x86_64.

As Fedora 35 comes along, we need to update our formatting as Fedora 35
comes with version "13.0.0~rc1-1.fc35".
An alternative would be to freeze on version 12, but that has different
problems (like, it's cumbersome to rebuild clang 12 on Fedora 35 and it
would be cumbersome for our developers which are on Fedora 35 to use a
clang that they cannot easily install).

The (differently painful) solution is to reformat from time to time, as we
switch to a new Fedora (and thus clang) version.
Usually we would expect that such a reformatting brings minor changes.
But this time, the changes are huge. That is mentioned in the release
notes [1] as

  Makes PointerAligment: Right working with AlignConsecutiveDeclarations. (Fixes https://llvm.org/PR27353)

[1] https://releases.llvm.org/13.0.0/tools/clang/docs/ReleaseNotes.html#clang-format
2021-11-29 09:31:09 +00:00

721 lines
26 KiB
C

/* 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);
}