summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2022-09-22 13:33:17 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2022-10-26 08:54:29 +0200
commit3f84ee27a0b5b756820dde192185a015eeeda1e0 (patch)
tree8d645f3fa42f0e15831db751615fb9dab22a92f7
parent06bf0707ee7328e7613ffa17a822b277e3d6b706 (diff)
platform: add mechanism to report removed IPv6 addresses that failed DAD
-rw-r--r--src/libnm-platform/nm-linux-platform.c8
-rw-r--r--src/libnm-platform/nm-platform.c99
-rw-r--r--src/libnm-platform/nm-platform.h6
3 files changed, 111 insertions, 2 deletions
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index ac137545c7..573c07d33e 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -7672,6 +7672,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:
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index c4922b16ed..f8f715bc23 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -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);
}
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index bcea5172f8..9b711254c9 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -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__ */