core: merge branch 'bg/ipv6-dad'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1366
This commit is contained in:
Beniamino Galvani 2022-10-26 13:31:03 +02:00
commit 63f4783c59
9 changed files with 268 additions and 103 deletions

View file

@ -4135,18 +4135,30 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N
/* Check if AC6 addresses completed DAD */
if (NM_FLAGS_ANY(notify_data->platform_change_on_idle.obj_type_flags,
nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP6_ADDRESS))
&& priv->ipac6_data.state == NM_DEVICE_IP_STATE_PENDING && priv->ipac6_data.l3cd
&& nm_l3cfg_check_ready(l3cfg,
priv->ipac6_data.l3cd,
AF_INET6,
NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY,
NULL)) {
if (nm_l3cfg_has_temp_not_available_obj(priv->l3cfg, AF_INET6))
_dev_l3_cfg_commit(self, FALSE);
&& priv->ipac6_data.state == NM_DEVICE_IP_STATE_PENDING && priv->ipac6_data.l3cd) {
gs_unref_array GArray *conflicts = NULL;
gboolean ready;
nm_clear_l3cd(&priv->ipac6_data.l3cd);
_dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY);
_dev_ip_state_check_async(self, AF_INET6);
ready = nm_l3cfg_check_ready(l3cfg,
priv->ipac6_data.l3cd,
AF_INET6,
NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY,
&conflicts);
if (conflicts) {
/* nm_ndisc_dad_failed() will emit a new "NDisc:config-received"
* signal; _dev_ipac6_ndisc_config_changed() will be called
* synchronously to update the current state and schedule a commit. */
nm_ndisc_dad_failed(priv->ipac6_data.ndisc, conflicts, TRUE);
} else if (ready) {
if (nm_l3cfg_has_temp_not_available_obj(priv->l3cfg, AF_INET6))
_dev_l3_cfg_commit(self, FALSE);
nm_clear_l3cd(&priv->ipac6_data.l3cd);
_dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY);
_dev_ip_state_check_async(self, AF_INET6);
} else {
/* wait */
}
}
_dev_ipmanual_check_ready(self);
@ -10079,7 +10091,7 @@ _dev_ipmanual_check_ready(NMDevice *self)
gboolean has_carrier;
NML3CfgCheckReadyFlags flags;
gboolean ready;
gboolean acd_used = FALSE;
gs_unref_array GArray *conflicts = NULL;
int IS_IPv4;
if (priv->ipmanual_data.state_4 != NM_DEVICE_IP_STATE_PENDING
@ -10122,8 +10134,8 @@ _dev_ipmanual_check_ready(NMDevice *self)
priv->l3cds[L3_CONFIG_DATA_TYPE_MANUALIP].d,
addr_family,
flags,
&acd_used);
if (acd_used) {
&conflicts);
if (conflicts) {
_dev_ipmanual_set_state(self, addr_family, NM_DEVICE_IP_STATE_FAILED);
_dev_ip_state_check_async(self, AF_UNSPEC);
} else if (ready) {
@ -11432,10 +11444,14 @@ _dev_ipac6_ndisc_config_changed(NMNDisc *ndisc,
const NML3ConfigData *l3cd,
NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
gboolean ready = TRUE;
NMDedupMultiIter iter;
const NMPObject *obj;
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
gboolean ready;
/* The ndisc configuration changes when we receive a new RA or
* when a lifetime expires; but also when DAD fails for a
* SLAAC address and we need to regenerate new stable-privacy
* addresses. In all these cases we update the AC6 configuration,
* schedule a commit and update the AC state. */
_dev_ipac6_grace_period_start(self, 0, TRUE);
@ -11446,22 +11462,11 @@ _dev_ipac6_ndisc_config_changed(NMNDisc *ndisc,
FALSE);
nm_clear_l3cd(&priv->ipac6_data.l3cd);
/* wait that addresses are committed to platform and
* become non-tentative before declaring AC6 is ready.*/
nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP6_ADDRESS) {
const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS(obj);
const NMPlatformIP6Address *plat_addr;
plat_addr = nm_platform_ip6_address_get(nm_device_get_platform(self),
nm_device_get_ip_ifindex(self),
&addr->address);
if (!plat_addr || (plat_addr->n_ifa_flags & IFA_F_TENTATIVE)) {
ready = FALSE;
break;
}
}
ready = nm_l3cfg_check_ready(priv->l3cfg,
l3cd,
AF_INET6,
NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY,
NULL);
if (ready) {
_dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY);
} else {

View file

@ -1039,35 +1039,33 @@ ipv6_lladdr_find(NMDhcpClient *self)
static void
ipv6_tentative_addr_check(NMDhcpClient *self,
GPtrArray **tentative,
GPtrArray **missing,
GPtrArray **dadfailed,
const NMPlatformIP6Address **valid)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
NMDedupMultiIter iter;
const NMPlatformIP6Address *addr;
NML3Cfg *l3cfg = priv->config.l3cfg;
NML3Cfg *l3cfg = priv->config.l3cfg;
NMPlatform *platform = nm_l3cfg_get_platform(l3cfg);
int ifindex = nm_l3cfg_get_ifindex(l3cfg);
/* For each address in the lease, check whether it's tentative
* in platform. */
* or dad-failed in platform. */
nm_l3_config_data_iter_ip6_address_for_each (&iter, priv->l3cd_curr, &addr) {
const NMPlatformIP6Address *pladdr;
NMPObject needle;
nmp_object_stackinit_id_ip6_address(&needle, nm_l3cfg_get_ifindex(l3cfg), &addr->address);
pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(nm_platform_lookup_obj(nm_l3cfg_get_platform(l3cfg),
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
&needle));
if (!pladdr) {
/* address removed: we assume that's because DAD failed */
if (missing) {
if (!*missing)
*missing = g_ptr_array_new();
g_ptr_array_add(*missing, (gpointer) addr);
pladdr = nm_platform_ip6_address_get(platform, ifindex, &addr->address);
if ((pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_DADFAILED))
|| (!pladdr && nm_platform_ip6_dadfailed_check(platform, ifindex, &addr->address))) {
if (dadfailed) {
if (!*dadfailed)
*dadfailed = g_ptr_array_new();
g_ptr_array_add(*dadfailed, (gpointer) addr);
}
continue;
}
if (NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE)
if (pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE)
&& !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
if (tentative) {
if (!*tentative)
@ -1076,6 +1074,9 @@ ipv6_tentative_addr_check(NMDhcpClient *self,
}
}
/* Here the address is non-tentative or it was removed externally by the user.
* In both cases it has completed DAD.
*/
NM_SET_OUT(valid, addr);
}
}
@ -1115,13 +1116,13 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE
&& priv->l3cfg_notify.wait_ipv6_dad) {
gs_unref_ptrarray GPtrArray *tentative = NULL;
gs_unref_ptrarray GPtrArray *missing = NULL;
gs_unref_ptrarray GPtrArray *dadfailed = NULL;
const NMPlatformIP6Address *valid = NULL;
char str[NM_UTILS_TO_STRING_BUFFER_SIZE];
guint i;
gs_free_error GError *error = NULL;
ipv6_tentative_addr_check(self, &tentative, &missing, &valid);
ipv6_tentative_addr_check(self, &tentative, &dadfailed, &valid);
if (tentative) {
for (i = 0; i < tentative->len; i++) {
_LOGD("still waiting DAD for address: %s",
@ -1134,10 +1135,10 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
l3_cfg_notify_check_connected(self);
if (missing) {
for (i = 0; i < missing->len; i++) {
if (dadfailed) {
for (i = 0; i < dadfailed->len; i++) {
_LOGE("DAD failed for address: %s",
nm_platform_ip6_address_to_string(missing->pdata[i], str, sizeof(str)));
nm_platform_ip6_address_to_string(dadfailed->pdata[i], str, sizeof(str)));
}
}

View file

@ -476,12 +476,12 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr)
priv->config.network_id,
addr->dad_counter++,
&error)) {
_LOGW("complete-address: failed to generate an stable-privacy address: %s",
_LOGW("complete-address: failed to generate a stable-privacy address: %s",
error->message);
g_clear_error(&error);
return FALSE;
}
_LOGD("complete-address: using an stable-privacy address");
_LOGD("complete-address: using a stable-privacy address");
return TRUE;
}
@ -496,7 +496,7 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr)
return TRUE;
}
_LOGW("complete-address: can't generate a new_item EUI-64 address");
_LOGW("complete-address: can't generate a new EUI-64 address");
return FALSE;
}
@ -1221,28 +1221,35 @@ nm_ndisc_stop(NMNDisc *ndisc)
}
NMNDiscConfigMap
nm_ndisc_dad_failed(NMNDisc *ndisc, const struct in6_addr *address, gboolean emit_changed_signal)
nm_ndisc_dad_failed(NMNDisc *ndisc, GArray *addresses, gboolean emit_changed_signal)
{
NMNDiscDataInternal *rdata;
guint i;
guint j;
gboolean changed = FALSE;
g_return_val_if_fail(addresses, NM_NDISC_CONFIG_NONE);
rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
for (i = 0; i < rdata->addresses->len;) {
NMNDiscAddress *item = &nm_g_array_index(rdata->addresses, NMNDiscAddress, i);
for (i = 0; i < addresses->len; i++) {
const struct in6_addr *addr = &nm_g_array_index(addresses, struct in6_addr, i);
if (IN6_ARE_ADDR_EQUAL(&item->address, address)) {
char sbuf[NM_INET_ADDRSTRLEN];
for (j = 0; j < rdata->addresses->len;) {
NMNDiscAddress *item = &nm_g_array_index(rdata->addresses, NMNDiscAddress, j);
_LOGD("DAD failed for discovered address %s", nm_inet6_ntop(address, sbuf));
changed = TRUE;
if (!complete_address(ndisc, item)) {
g_array_remove_index(rdata->addresses, i);
continue;
if (IN6_ARE_ADDR_EQUAL(&item->address, addr)) {
char sbuf[NM_INET_ADDRSTRLEN];
_LOGI("DAD failed for discovered address %s", nm_inet6_ntop(addr, sbuf));
changed = TRUE;
if (!complete_address(ndisc, item)) {
g_array_remove_index(rdata->addresses, j);
continue;
}
}
j++;
}
i++;
}
if (emit_changed_signal && changed)

View file

@ -228,9 +228,9 @@ NMNDiscNodeType nm_ndisc_get_node_type(NMNDisc *self);
gboolean nm_ndisc_set_iid(NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid, gboolean is_token);
void nm_ndisc_start(NMNDisc *ndisc);
void nm_ndisc_stop(NMNDisc *ndisc);
void nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd);
NMNDiscConfigMap
nm_ndisc_dad_failed(NMNDisc *ndisc, const struct in6_addr *address, gboolean emit_changed_signal);
void nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd);
nm_ndisc_dad_failed(NMNDisc *ndisc, GArray *addresses, gboolean emit_changed_signal);
NMPlatform *nm_ndisc_get_platform(NMNDisc *self);
NMPNetns *nm_ndisc_netns_get(NMNDisc *self);

View file

@ -2990,68 +2990,103 @@ nm_l3cfg_check_ready(NML3Cfg *self,
const NML3ConfigData *l3cd,
int addr_family,
NML3CfgCheckReadyFlags flags,
gboolean *acd_used)
GArray **conflicts)
{
NMDedupMultiIter iter;
const NMPObject *obj;
gboolean ready = TRUE;
nm_assert(NM_IS_L3CFG(self));
nm_assert_addr_family_or_unspec(addr_family);
NM_SET_OUT(acd_used, FALSE);
nm_assert_addr_family(addr_family);
nm_assert(!conflicts || !*conflicts);
if (!l3cd)
return TRUE;
if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)
&& NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP4_ACD_READY)) {
gboolean pending = FALSE;
if (addr_family == AF_INET) {
if (NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP4_ACD_READY)) {
nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS) {
const NML3AcdAddrInfo *addr_info;
nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP4_ADDRESS) {
const NML3AcdAddrInfo *addr_info;
addr_info = nm_l3cfg_get_acd_addr_info(self, NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address);
if (!addr_info) {
/* We don't track the this address? That's odd. Not ready. */
pending = TRUE;
} else {
if (addr_info->state <= NM_L3_ACD_ADDR_STATE_PROBING) {
/* Still probing. Not ready. */
pending = TRUE;
} else if (addr_info->state == NM_L3_ACD_ADDR_STATE_USED) {
NM_SET_OUT(acd_used, TRUE);
addr_info =
nm_l3cfg_get_acd_addr_info(self, NMP_OBJECT_CAST_IP4_ADDRESS(obj)->address);
if (!addr_info) {
/* We don't track the this address? That's odd. Not ready. */
ready = FALSE;
} else {
if (addr_info->state <= NM_L3_ACD_ADDR_STATE_PROBING) {
/* Still probing. Not ready. */
ready = FALSE;
} else if (addr_info->state == NM_L3_ACD_ADDR_STATE_USED) {
if (conflicts) {
if (!*conflicts)
*conflicts = g_array_new(FALSE,
FALSE,
nm_utils_addr_family_to_size(addr_family));
g_array_append_val(*conflicts, addr_info->addr);
}
}
}
/* we only care that we don't have ACD still pending. Otherwise we are ready,
* including if we have no addr_info about this address or the address is in use. */
}
/* we only care that we don't have ACD still pending. Otherwise we are ready,
* including if we have no addr_info about this address or the address is in use. */
}
if (pending)
return FALSE;
return ready;
}
if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)
&& NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY)) {
/* AF_INET6 */
if (NM_FLAGS_HAS(flags, NM_L3CFG_CHECK_READY_FLAGS_IP6_DAD_READY)) {
nm_l3_config_data_iter_obj_for_each (&iter, l3cd, &obj, NMP_OBJECT_TYPE_IP6_ADDRESS) {
ObjStateData *obj_state;
gboolean dadfailed = FALSE;
obj_state = g_hash_table_lookup(self->priv.p->obj_state_hash, &obj);
if (!obj_state) {
/* Hm, we don't track this object? That is odd. Not ready. */
return FALSE;
ready = FALSE;
continue;
}
if (!obj_state->os_nm_configured && !obj_state->os_plobj) {
/* We didn't (yet) configure this address and it also is not in platform.
* Not ready. */
return FALSE;
ready = FALSE;
continue;
}
if (obj_state->os_plobj
&& NM_FLAGS_HAS(NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->os_plobj)->n_ifa_flags,
IFA_F_DADFAILED)) {
/* The address is still present with DADFAILED flag. */
dadfailed = TRUE;
} else if (obj_state->os_nm_configured && !obj_state->os_plobj
&& nm_platform_ip6_dadfailed_check(
self->priv.platform,
self->priv.ifindex,
&NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->obj)->address)) {
/* We configured the address and kernel removed it with DADFAILED flag. */
dadfailed = TRUE;
}
if (dadfailed) {
if (conflicts) {
if (!*conflicts) {
*conflicts =
g_array_new(FALSE, FALSE, nm_utils_addr_family_to_size(addr_family));
}
g_array_append_val(*conflicts,
NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->obj)->address);
}
continue;
}
if (obj_state->os_plobj
&& NM_FLAGS_HAS(NMP_OBJECT_CAST_IP6_ADDRESS(obj_state->os_plobj)->n_ifa_flags,
IFA_F_TENTATIVE)) {
/* The address is configured in kernel, but still tentative. Not ready. */
return FALSE;
ready = FALSE;
continue;
}
/* This address is ready. Even if it is not (not anymore) configured in kernel (as
@ -3060,7 +3095,7 @@ nm_l3cfg_check_ready(NML3Cfg *self,
}
}
return TRUE;
return ready;
}
/*****************************************************************************/

View file

@ -400,7 +400,7 @@ gboolean nm_l3cfg_check_ready(NML3Cfg *self,
const NML3ConfigData *l3cd,
int addr_family,
NML3CfgCheckReadyFlags flags,
gboolean *acd_used);
GArray **conflicts);
gboolean nm_l3cfg_has_temp_not_available_obj(NML3Cfg *self, int addr_family);

View file

@ -6759,6 +6759,14 @@ cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup)
obj = main_entry->obj;
if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ADDRESS) {
const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj);
if (pladdr->n_ifa_flags & IFA_F_TENTATIVE) {
nm_platform_ip6_dadfailed_set(platform, pladdr->ifindex, &pladdr->address, TRUE);
}
}
_LOGt("cache-prune: prune %s",
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf)));
@ -7672,6 +7680,14 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg)
}
case RTM_DELADDR:
if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ADDRESS) {
const NMPlatformIP6Address *ip6 = NMP_OBJECT_CAST_IP6_ADDRESS(obj);
if (ip6->n_ifa_flags & IFA_F_DADFAILED) {
nm_platform_ip6_dadfailed_set(platform, ip6->ifindex, &ip6->address, TRUE);
}
}
/* fall-through */
case RTM_DELLINK:
case RTM_DELQDISC:
case RTM_DELROUTE:

View file

@ -195,6 +195,7 @@ typedef struct _NMPlatformPrivate {
guint ip4_dev_route_blacklist_check_id;
guint ip4_dev_route_blacklist_gc_timeout_id;
GHashTable *ip4_dev_route_blacklist_hash;
CList ip6_dadfailed_lst_head;
NMDedupMultiIndex *multi_idx;
NMPCache *cache;
} NMPlatformPrivate;
@ -3607,6 +3608,9 @@ nm_platform_ip6_address_add(NMPlatform *self,
_LOG3D("address: adding or updating IPv6 address: %s",
nm_platform_ip6_address_to_string(&addr, sbuf, sizeof(sbuf)));
}
nm_platform_ip6_dadfailed_set(self, ifindex, &address, FALSE);
return klass
->ip6_address_add(self, ifindex, address, plen, peer_address, lifetime, preferred, flags);
}
@ -9089,6 +9093,92 @@ nm_platform_netns_push(NMPlatform *self, NMPNetns **netns)
/*****************************************************************************/
typedef struct {
struct in6_addr address;
CList lst;
gint64 timestamp_nsec;
int ifindex;
} IP6DadFailedAddr;
static void
ip6_dadfailed_addr_free(IP6DadFailedAddr *addr)
{
c_list_unlink_stale(&addr->lst);
nm_g_slice_free(addr);
}
static void
ip6_dadfailed_prune_old(NMPlatform *self, gint64 now_nsec)
{
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
IP6DadFailedAddr *addr;
IP6DadFailedAddr *safe;
c_list_for_each_entry_safe (addr, safe, &priv->ip6_dadfailed_lst_head, lst) {
if (addr->timestamp_nsec + (10 * NM_UTILS_NSEC_PER_SEC) > now_nsec)
break;
ip6_dadfailed_addr_free(addr);
}
}
gboolean
nm_platform_ip6_dadfailed_check(NMPlatform *self, int ifindex, const struct in6_addr *ip6)
{
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
IP6DadFailedAddr *addr;
ip6_dadfailed_prune_old(self, nm_utils_get_monotonic_timestamp_nsec());
c_list_for_each_entry_prev (addr, &priv->ip6_dadfailed_lst_head, lst) {
if (addr->ifindex == ifindex && IN6_ARE_ADDR_EQUAL(&addr->address, ip6)) {
return TRUE;
}
}
return FALSE;
}
/*
* If an IPv6 address fails DAD and has infinite lifetime, kernel just
* sets the DADFAILED flag. However when the address has a finite
* lifetime kernel deletes it immediately and the RTM_DELLINK netlink
* message contains the DADFAILED flag. In the second case, we remove
* the address from the platform cache and there is no way for
* platform's clients to check whether DAD failed. To work around
* this, we store all deleted-with-DADFAILED addresses and provide a
* mechanism to access them.
*/
void
nm_platform_ip6_dadfailed_set(NMPlatform *self,
int ifindex,
const struct in6_addr *ip6,
gboolean failed)
{
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
gint64 now_nsec = nm_utils_get_monotonic_timestamp_nsec();
IP6DadFailedAddr *addr;
IP6DadFailedAddr *safe;
ip6_dadfailed_prune_old(self, now_nsec);
if (failed) {
addr = g_slice_new(IP6DadFailedAddr);
*addr = (IP6DadFailedAddr){
.address = *ip6,
.ifindex = ifindex,
.timestamp_nsec = now_nsec,
};
c_list_link_tail(&priv->ip6_dadfailed_lst_head, &addr->lst);
} else {
c_list_for_each_entry_safe (addr, safe, &priv->ip6_dadfailed_lst_head, lst) {
if (addr->ifindex == ifindex && IN6_ARE_ADDR_EQUAL(&addr->address, ip6)) {
ip6_dadfailed_addr_free(addr);
}
}
}
}
/*****************************************************************************/
const _NMPlatformVTableRouteUnion nm_platform_vtable_route = {
.v4 =
{
@ -9174,8 +9264,8 @@ constructor(GType type, guint n_construct_params, GObjectConstructParam *constru
priv = NM_PLATFORM_GET_PRIVATE(self);
priv->multi_idx = nm_dedup_multi_index_new();
priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev);
priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev);
c_list_init(&priv->ip6_dadfailed_lst_head);
return object;
}
@ -9185,6 +9275,7 @@ finalize(GObject *object)
{
NMPlatform *self = NM_PLATFORM(object);
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
IP6DadFailedAddr *addr;
nm_clear_g_source(&priv->ip4_dev_route_blacklist_check_id);
nm_clear_g_source(&priv->ip4_dev_route_blacklist_gc_timeout_id);
@ -9193,6 +9284,10 @@ finalize(GObject *object)
nm_dedup_multi_index_unref(priv->multi_idx);
nmp_cache_free(priv->cache);
while ((addr = c_list_first_entry(&priv->ip6_dadfailed_lst_head, IP6DadFailedAddr, lst))) {
ip6_dadfailed_addr_free(addr);
}
G_OBJECT_CLASS(nm_platform_parent_class)->finalize(object);
}

View file

@ -2386,4 +2386,10 @@ nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatfo
GPtrArray *nm_platform_mptcp_addrs_dump(NMPlatform *self);
gboolean nm_platform_ip6_dadfailed_check(NMPlatform *self, int ifindex, const struct in6_addr *ip6);
void nm_platform_ip6_dadfailed_set(NMPlatform *self,
int ifindex,
const struct in6_addr *ip6,
gboolean failed);
#endif /* __NETWORKMANAGER_PLATFORM_H__ */