diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index a7189d3e60..0d20bf44a5 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -370,6 +370,8 @@ G_DEFINE_TYPE(NML3Cfg, nm_l3cfg, G_TYPE_OBJECT) #define _NETNS_WATCHER_IP_ADDR_TAG(self, addr_family) \ ((gconstpointer) & (((char *) self)[1 + NM_IS_IPv4(addr_family)])) +#define _NETNS_WATCHER_MPTCP_IPV6_TAG(self) ((gconstpointer) & (((char *) self)[3])) + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_CORE @@ -4764,7 +4766,7 @@ next: } out: - nm_netns_watcher_remove_all(self->priv.netns, TAG, FALSE); + nm_netns_watcher_remove_dirty(self->priv.netns, TAG); } /*****************************************************************************/ @@ -4777,6 +4779,30 @@ _global_tracker_mptcp_untrack(NML3Cfg *self, int addr_family) TRUE); } +static void +mptcp_ipv6_addr_cb(NMNetns *netns, + NMNetnsWatcherType watcher_type, + const NMNetnsWatcherData *watcher_data, + gconstpointer tag, + const NMNetnsWatcherEventData *event_data, + gpointer user_data) +{ + NML3Cfg *self = user_data; + + if (event_data->ip_addr.change_type == NM_PLATFORM_SIGNAL_REMOVED) + return; + + nm_assert(NMP_OBJECT_GET_TYPE(event_data->ip_addr.obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); + + if (event_data->ip_addr.obj->ip6_address.n_ifa_flags & IFA_F_TENTATIVE) + return; + + /* We are inside the handler for a platform event, we should not + * perform other operations on platform synchronously. Schedule a + * commit in a idle handler. */ + nm_l3cfg_commit_on_idle_schedule(self, NM_L3_CFG_COMMIT_TYPE_AUTO); +} + static gboolean _l3_commit_mptcp_af(NML3Cfg *self, NML3CfgCommitType commit_type, @@ -4855,6 +4881,8 @@ _l3_commit_mptcp_af(NML3Cfg *self, self->priv.p->combined_l3cd_commited, addr_family, (const NMPlatformIPAddress **) &addr) { + const NMPObject *obj; + /* We want to evaluate the with-{loopback,link_local}-{4,6} flags based on the actual * ifa_scope that the address will have once we configure it. * "addr" is an address we want to configure, we expect that it will @@ -4881,6 +4909,34 @@ _l3_commit_mptcp_af(NML3Cfg *self, break; } + obj = nm_platform_ip_address_get(self->priv.platform, + addr_family, + self->priv.ifindex, + addr); + if (!obj) { + /* The address is not yet configured in platform, typically due to + * IPv4 DAD; skip it for now otherwise the kernel will try to use + * the endpoint, it will fail, and it will never try it again. */ + goto skip_addr; + } + if (!IS_IPv4 && (obj->ip6_address.n_ifa_flags & IFA_F_TENTATIVE)) { + NMNetnsWatcherData watcher_data = {}; + + /* The endpoint is not usable when the address is tentative. + * Watch the address until it becomes non-tentative and then + * schedule a new commit. */ + watcher_data.ip_addr.addr.addr_family = AF_INET6; + watcher_data.ip_addr.addr.addr.addr6 = addr->a6.address; + + nm_netns_watcher_add(self->priv.netns, + NM_NETNS_WATCHER_TYPE_IP_ADDR, + &watcher_data, + _NETNS_WATCHER_MPTCP_IPV6_TAG(self), + mptcp_ipv6_addr_cb, + self); + goto skip_addr; + } + a.addr = nm_ip_addr_init(addr_family, addr->ax.address_ptr); /* We track the address with different priorities, that depends @@ -4909,6 +4965,8 @@ skip_addr: } } + nm_netns_watcher_remove_dirty(self->priv.netns, _NETNS_WATCHER_MPTCP_IPV6_TAG(self)); + if (!any_tracked) { /* We need to make it known that this ifindex is used. Track a dummy object. */ if (nmp_global_tracker_track( @@ -5636,12 +5694,9 @@ finalize(GObject *object) gboolean changed; if (self->priv.netns) { - nm_netns_watcher_remove_all(self->priv.netns, - _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET), - TRUE); - nm_netns_watcher_remove_all(self->priv.netns, - _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET6), - TRUE); + nm_netns_watcher_remove_all(self->priv.netns, _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET)); + nm_netns_watcher_remove_all(self->priv.netns, _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET6)); + nm_netns_watcher_remove_all(self->priv.netns, _NETNS_WATCHER_MPTCP_IPV6_TAG(self)); } nm_prioq_destroy(&self->priv.p->failedobj_prioq); diff --git a/src/core/nm-netns.c b/src/core/nm-netns.c index ed33d33676..4d28656142 100644 --- a/src/core/nm-netns.c +++ b/src/core/nm-netns.c @@ -1369,8 +1369,8 @@ nm_netns_watcher_remove_handle(NMNetns *self, NMNetnsWatcherHandle *handle) g_object_unref(self); } -void -nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag, gboolean all) +static void +watcher_remove(NMNetns *self, gconstpointer tag, gboolean all) { NMNetnsPrivate *priv; WatcherByTag *watcher_by_tag; @@ -1420,6 +1420,21 @@ nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag, gboolean all) } } +void +nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag) +{ + watcher_remove(self, tag, TRUE); +} + +/* Similar to nm_netns_watcher_remove_all(), but removes only watchers + * that were marked as "dirty" in a previous call of this function and were + * not added back via nm_netns_watcher_add() in the meantime. */ +void +nm_netns_watcher_remove_dirty(NMNetns *self, gconstpointer tag) +{ + watcher_remove(self, tag, FALSE); +} + /*****************************************************************************/ static void diff --git a/src/core/nm-netns.h b/src/core/nm-netns.h index 7725ae79b4..43e9c7818a 100644 --- a/src/core/nm-netns.h +++ b/src/core/nm-netns.h @@ -98,7 +98,7 @@ void nm_netns_watcher_add(NMNetns *self, NMNetnsWatcherCallback callback, gpointer user_data); -void -nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag, gboolean all /* or only dirty */); +void nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag); +void nm_netns_watcher_remove_dirty(NMNetns *self, gconstpointer tag); #endif /* __NM_NETNS_H__ */