Add support for CLAT to l3cfg (fixes)

This commit is contained in:
Beniamino Galvani 2025-09-08 21:51:40 +02:00
parent f0e77a4354
commit 76c18081d6
4 changed files with 126 additions and 103 deletions

View file

@ -304,6 +304,7 @@ typedef enum {
NM_UTILS_STABLE_TYPE_STABLE_ID = 1, NM_UTILS_STABLE_TYPE_STABLE_ID = 1,
NM_UTILS_STABLE_TYPE_GENERATED = 2, NM_UTILS_STABLE_TYPE_GENERATED = 2,
NM_UTILS_STABLE_TYPE_RANDOM = 3, NM_UTILS_STABLE_TYPE_RANDOM = 3,
NM_UTILS_STABLE_TYPE_CLAT = 4,
} NMUtilsStableType; } NMUtilsStableType;
#define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) - 1) #define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) - 1)

View file

@ -2001,12 +2001,11 @@ nm_l3_config_data_set_pref64_valid(NML3ConfigData *self, gboolean val)
} }
gboolean gboolean
nm_l3_config_data_get_pref64_valid(const NML3ConfigData *self, gboolean *out_val) nm_l3_config_data_get_pref64_valid(const NML3ConfigData *self)
{ {
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
NM_SET_OUT(out_val, self->pref64_valid); return self->pref64_valid;
return TRUE;
} }
gboolean gboolean

View file

@ -500,7 +500,7 @@ const char *nm_l3_config_data_get_network_id(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_pref64_valid(NML3ConfigData *self, gboolean val); gboolean nm_l3_config_data_set_pref64_valid(NML3ConfigData *self, gboolean val);
gboolean nm_l3_config_data_get_pref64_valid(const NML3ConfigData *self, gboolean *out_val); gboolean nm_l3_config_data_get_pref64_valid(const NML3ConfigData *self);
gboolean nm_l3_config_data_get_pref64(const NML3ConfigData *self, gboolean nm_l3_config_data_get_pref64(const NML3ConfigData *self,
struct in6_addr *out_prefix, struct in6_addr *out_prefix,

View file

@ -11,7 +11,7 @@
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/fib_rules.h> #include <linux/fib_rules.h>
#ifdef HAVE_CLAT #if HAVE_CLAT
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include <bpf/bpf.h> #include <bpf/bpf.h>
#endif /* HAVE_CLAT */ #endif /* HAVE_CLAT */
@ -27,7 +27,7 @@
#include "nm-l3-ipv4ll.h" #include "nm-l3-ipv4ll.h"
#include "nm-ip-config.h" #include "nm-ip-config.h"
#include "nm-core-utils.h" #include "nm-core-utils.h"
#ifdef HAVE_CLAT #if HAVE_CLAT
#include "bpf/clat.h" #include "bpf/clat.h"
NM_PRAGMA_WARNING_DISABLE("-Wcast-align") NM_PRAGMA_WARNING_DISABLE("-Wcast-align")
#include "bpf/clat.skel.h" #include "bpf/clat.skel.h"
@ -300,12 +300,19 @@ typedef struct _NML3CfgPrivate {
NMIPConfig *ipconfig_x[2]; NMIPConfig *ipconfig_x[2];
}; };
#ifdef HAVE_CLAT #if HAVE_CLAT
NMNetnsIPRange *clat_address_4_commited; /* The reserved IPv4 address for CLAT in the 192.0.0.0/28 range */
NMPlatformIP6Address clat_address_6_commited; NMNetnsIPReservation *clat_address_4;
NMNetnsIPRange *clat_address_4; /* The IPv6 address for sending and receiving translated packets */
NMPlatformIP6Address clat_address_6; NMPlatformIP6Address clat_address_6;
struct in6_addr clat_pref64;
/* The same addresses as above, but already committed previously */
NMNetnsIPReservation *clat_address_4_committed;
NMPlatformIP6Address clat_address_6_committed;
/* The NAT64 prefix discovered via RA */
struct in6_addr clat_pref64;
/* If NULL, the BPF program hasn't been loaded or attached */ /* If NULL, the BPF program hasn't been loaded or attached */
struct clat_bpf *clat_bpf; struct clat_bpf *clat_bpf;
int clat_socket; int clat_socket;
@ -379,7 +386,7 @@ typedef struct _NML3CfgPrivate {
bool rp_filter_set : 1; bool rp_filter_set : 1;
bool clat_address_6_valid : 1; bool clat_address_6_valid : 1;
bool clat_address_6_commited_valid : 1; bool clat_address_6_committed_valid : 1;
bool clat_pref64_valid : 1; bool clat_pref64_valid : 1;
} NML3CfgPrivate; } NML3CfgPrivate;
@ -4151,9 +4158,10 @@ _l3cfg_update_combined_config(NML3Cfg *self,
guint i; guint i;
gboolean merged_changed = FALSE; gboolean merged_changed = FALSE;
gboolean commited_changed = FALSE; gboolean commited_changed = FALSE;
gboolean pref64_valid = FALSE; #if HAVE_CLAT
struct in6_addr pref64; struct in6_addr pref64;
guint32 pref64_plen; guint32 pref64_plen;
#endif
nm_assert(NM_IS_L3CFG(self)); nm_assert(NM_IS_L3CFG(self));
nm_assert(!out_old || !*out_old); nm_assert(!out_old || !*out_old);
@ -4252,32 +4260,42 @@ _l3cfg_update_combined_config(NML3Cfg *self,
} }
#if HAVE_CLAT #if HAVE_CLAT
nm_assert(nm_l3_config_data_get_pref64_valid(l3cd, &pref64_valid)); if (nm_l3_config_data_get_pref64_valid(l3cd)) {
if (pref64_valid) { NMPlatformIPXRoute rx;
nm_assert(nm_l3_config_data_get_pref64(l3cd, &pref64, &pref64_plen)); NMIPAddrTyped best_v6_gateway;
} NMDedupMultiIter iter;
if (pref64_valid) {
NMPlatformIPXRoute rx;
NMIPAddrTyped best_v6_gateway;
struct in6_addr ip6;
NMDedupMultiIter iter;
const NMPlatformIP6Route *best_v6_route; const NMPlatformIP6Route *best_v6_route;
const NMPlatformIP6Address *ip6_entry; const NMPlatformIP6Address *ip6_entry;
struct in6_addr ip6;
const char *network_id;
char buf[512];
/* We need to assign an additional v6 /64 address for ourselves, according /* If we have a valid NAT64 prefix, configure in kernel:
to the spec: https://www.rfc-editor.org/rfc/rfc6877#section-6.3 */ *
if (!self->priv.p->clat_address_6_valid) { * - a CLAT IPv4 address (192.0.0.x)
* - a IPv4 default route via the best IPv6 gateway
*
* We also set clat_address_6 as an additional /64 IPv6 address
* determined according to https://www.rfc-editor.org/rfc/rfc6877#section-6.3 .
* This address is used for sending and receiving translated packets,
* but is not configured in kernel to avoid that it gets used by applications.
* Later in _l3_commit_pref64() we use IPV6_JOIN_ANYCAST to let the kernel
* handle ND for the address.
*/
nm_l3_config_data_get_pref64(l3cd, &pref64, &pref64_plen);
network_id = nm_l3_config_data_get_network_id(l3cd);
if (!self->priv.p->clat_address_6_valid && network_id) {
nm_l3_config_data_iter_ip6_address_for_each (&iter, l3cd, &ip6_entry) { nm_l3_config_data_iter_ip6_address_for_each (&iter, l3cd, &ip6_entry) {
if (ip6_entry->addr_source == NM_IP_CONFIG_SOURCE_NDISC if (ip6_entry->addr_source == NM_IP_CONFIG_SOURCE_NDISC
&& ip6_entry->plen == 64) { && ip6_entry->plen == 64) {
ip6 = ip6_entry->address; ip6 = ip6_entry->address;
nm_utils_ipv6_addr_set_stable_privacy(NM_UTILS_STABLE_TYPE_RANDOM, nm_utils_ipv6_addr_set_stable_privacy(NM_UTILS_STABLE_TYPE_CLAT,
&ip6, &ip6,
nm_l3cfg_get_ifname(self, TRUE), nm_l3cfg_get_ifname(self, TRUE),
"TODO: Mary fixme", network_id,
0); 0);
self->priv.p->clat_address_6 = (NMPlatformIP6Address) { self->priv.p->clat_address_6 = (NMPlatformIP6Address) {
.ifindex = self->priv.ifindex, .ifindex = self->priv.ifindex,
@ -4286,6 +4304,9 @@ _l3cfg_update_combined_config(NML3Cfg *self,
.addr_source = NM_IP_CONFIG_SOURCE_CLAT, .addr_source = NM_IP_CONFIG_SOURCE_CLAT,
.plen = ip6_entry->plen, .plen = ip6_entry->plen,
}; };
_LOGT("clat: using IPv6 address %s", nm_inet6_ntop(&ip6, buf));
self->priv.p->clat_address_6_valid = TRUE; self->priv.p->clat_address_6_valid = TRUE;
break; break;
} }
@ -4296,16 +4317,18 @@ _l3cfg_update_combined_config(NML3Cfg *self,
potentially create broken v4 connectivity) */ potentially create broken v4 connectivity) */
if (!self->priv.p->clat_address_6_valid) { if (!self->priv.p->clat_address_6_valid) {
_LOGW("CLAT is currently only supported when SLAAC is in use."); _LOGW("CLAT is currently only supported when SLAAC is in use.");
/* Deallocate the v4 address unless it's the commited one */ /* Deallocate the v4 address unless it's the committed one */
if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_commited) { if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_committed) {
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_range_release); nm_clear_pointer(&self->priv.p->clat_address_4,
nm_netns_ip_reservation_release);
} else { } else {
self->priv.p->clat_address_4 = NULL; self->priv.p->clat_address_4 = NULL;
} }
} else if (!self->priv.p->clat_address_4) { } else if (!self->priv.p->clat_address_4) {
/* We need a v4 /32 */ /* We need a v4 /32 */
self->priv.p->clat_address_4 = self->priv.p->clat_address_4 =
nm_netns_ip_range_allocate(self->priv.netns, NM_NETNS_IP_RANGE_TYPE_CLAT); nm_netns_ip_reservation_get(self->priv.netns,
NM_NETNS_IP_RESERVATION_TYPE_CLAT);
} }
if (self->priv.p->clat_address_4) { if (self->priv.p->clat_address_4) {
@ -4357,6 +4380,10 @@ _l3cfg_update_combined_config(NML3Cfg *self,
if (!nm_l3_config_data_lookup_route(l3cd, AF_INET, &rx.rx)) { if (!nm_l3_config_data_lookup_route(l3cd, AF_INET, &rx.rx)) {
nm_l3_config_data_add_route_4(l3cd, &rx.r4); nm_l3_config_data_add_route_4(l3cd, &rx.r4);
} }
_LOGT("clat: route %s",
nm_platform_ip4_route_to_string(&rx.r4, buf, sizeof(buf)));
nm_l3_config_data_add_address_4(l3cd, &addr); nm_l3_config_data_add_address_4(l3cd, &addr);
} else { } else {
_LOGW("Couldn't find a good ipv6 route! Unable to set up CLAT!"); _LOGW("Couldn't find a good ipv6 route! Unable to set up CLAT!");
@ -4425,15 +4452,17 @@ _l3cfg_update_combined_config(NML3Cfg *self,
if (nm_l3_config_data_equal(l3cd, self->priv.p->combined_l3cd_merged)) if (nm_l3_config_data_equal(l3cd, self->priv.p->combined_l3cd_merged))
goto out; goto out;
#if HAVE_CLAT
if (!l3cd) { if (!l3cd) {
self->priv.p->clat_address_6_valid = FALSE; self->priv.p->clat_address_6_valid = FALSE;
/* Deallocate the v4 address unless it's the commited one */ /* Deallocate the v4 address unless it's the commited one */
if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_commited) { if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_committed) {
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_range_release); nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_reservation_release);
} else { } else {
self->priv.p->clat_address_4 = NULL; self->priv.p->clat_address_4 = NULL;
} }
} }
#endif /* HAVE_CLAT */
l3cd_old = g_steal_pointer(&self->priv.p->combined_l3cd_merged); l3cd_old = g_steal_pointer(&self->priv.p->combined_l3cd_merged);
self->priv.p->combined_l3cd_merged = nm_l3_config_data_seal(g_steal_pointer(&l3cd)); self->priv.p->combined_l3cd_merged = nm_l3_config_data_seal(g_steal_pointer(&l3cd));
@ -5534,14 +5563,14 @@ _l3_commit_one(NML3Cfg *self,
_failedobj_handle_routes(self, addr_family, routes_failed); _failedobj_handle_routes(self, addr_family, routes_failed);
} }
#ifdef HAVE_CLAT #if HAVE_CLAT
static void static void
_l3_get_pref64_config(NML3Cfg *self, _l3_get_pref64_config(NML3Cfg *self,
struct clat_v4_config_key *v4_key, struct clat_v4_config_key *v4_key,
struct clat_v4_config_value *v4_value, struct clat_v4_config_value *v4_value,
struct clat_v6_config_key *v6_key, struct clat_v6_config_key *v6_key,
struct clat_v6_config_value *v6_value, struct clat_v6_config_value *v6_value,
gboolean commited) gboolean committed)
{ {
const NML3ConfigData *l3cd = self->priv.p->combined_l3cd_commited; const NML3ConfigData *l3cd = self->priv.p->combined_l3cd_commited;
struct in6_addr pref64; struct in6_addr pref64;
@ -5554,13 +5583,13 @@ _l3_get_pref64_config(NML3Cfg *self,
v4_key->ifindex = v6_key->ifindex = self->priv.ifindex; v4_key->ifindex = v6_key->ifindex = self->priv.ifindex;
if (commited) { if (committed) {
if (self->priv.p->clat_address_4_commited) { if (self->priv.p->clat_address_4_committed) {
v6_value->local_v4.s_addr = v4_key->local_v4.s_addr = v6_value->local_v4.s_addr = v4_key->local_v4.s_addr =
self->priv.p->clat_address_4_commited->addr; self->priv.p->clat_address_4_committed->addr;
} }
if (self->priv.p->clat_address_6_commited_valid) { if (self->priv.p->clat_address_6_committed_valid) {
v4_value->local_v6 = v6_key->local_v6 = self->priv.p->clat_address_6_commited.address; v4_value->local_v6 = v6_key->local_v6 = self->priv.p->clat_address_6_committed.address;
} }
if (self->priv.p->clat_pref64_valid) { if (self->priv.p->clat_pref64_valid) {
v6_key->pref64 = v4_value->pref64 = self->priv.p->clat_pref64; v6_key->pref64 = v4_value->pref64 = self->priv.p->clat_pref64;
@ -5605,6 +5634,9 @@ _l3_clat_destroy(NML3Cfg *self)
libbpf_strerror(err, buf, sizeof(buf)); libbpf_strerror(err, buf, sizeof(buf));
_LOGW("failed to tear down CLAT BPF egress hook: %s", buf); _LOGW("failed to tear down CLAT BPF egress hook: %s", buf);
} }
bpf_tc_hook_destroy(&hook);
nm_clear_pointer(&self->priv.p->clat_bpf, clat_bpf__destroy); nm_clear_pointer(&self->priv.p->clat_bpf, clat_bpf__destroy);
} }
@ -5617,10 +5649,10 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
const struct in6_addr *l3cd_pref64 = NULL; const struct in6_addr *l3cd_pref64 = NULL;
guint32 l3cd_pref64_plen; guint32 l3cd_pref64_plen;
char buf[100]; char buf[100];
struct clat_v6_config_key v6_key_commited, v6_key_new; struct clat_v6_config_key v6_key_committed, v6_key_new;
struct clat_v4_config_key v4_key_commited, v4_key_new; struct clat_v4_config_key v4_key_committed, v4_key_new;
struct clat_v6_config_value v6_value_commited, v6_value_new; struct clat_v6_config_value v6_value_committed, v6_value_new;
struct clat_v4_config_value v4_value_commited, v4_value_new; struct clat_v4_config_value v4_value_committed, v4_value_new;
gboolean v6_changed; gboolean v6_changed;
DECLARE_LIBBPF_OPTS(bpf_tc_hook, DECLARE_LIBBPF_OPTS(bpf_tc_hook,
hook, hook,
@ -5635,12 +5667,12 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
if (l3cd_pref64 && self->priv.p->clat_address_4 && self->priv.p->clat_address_6_valid) { if (l3cd_pref64 && self->priv.p->clat_address_4 && self->priv.p->clat_address_6_valid) {
if (!self->priv.p->clat_bpf) { if (!self->priv.p->clat_bpf) {
/* Bring up the CLAT */ _LOGT("clat: attaching the BPF program");
self->priv.p->clat_bpf = clat_bpf__open(); self->priv.p->clat_bpf = clat_bpf__open();
err = libbpf_get_error(self->priv.p->clat_bpf); err = libbpf_get_error(self->priv.p->clat_bpf);
if (err) { if (err) {
libbpf_strerror(err, buf, sizeof(buf)); libbpf_strerror(err, buf, sizeof(buf));
_LOGW("failed to load CLAT BPF: %s", buf); _LOGW("clat: failed to open the BPF program: %s", buf);
clat_bpf__destroy(self->priv.p->clat_bpf); clat_bpf__destroy(self->priv.p->clat_bpf);
self->priv.p->clat_bpf = NULL; self->priv.p->clat_bpf = NULL;
return; return;
@ -5653,7 +5685,7 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
err = clat_bpf__load(self->priv.p->clat_bpf); err = clat_bpf__load(self->priv.p->clat_bpf);
if (err) { if (err) {
libbpf_strerror(err, buf, sizeof(buf)); libbpf_strerror(err, buf, sizeof(buf));
_LOGW("failed to open CLAT BPF: %s", buf); _LOGW("clat: failed to load the BPF program: %s", buf);
clat_bpf__destroy(self->priv.p->clat_bpf); clat_bpf__destroy(self->priv.p->clat_bpf);
self->priv.p->clat_bpf = NULL; self->priv.p->clat_bpf = NULL;
return; return;
@ -5662,7 +5694,7 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
err = bpf_tc_hook_create(&hook); err = bpf_tc_hook_create(&hook);
if (err && err != -EEXIST) { if (err && err != -EEXIST) {
libbpf_strerror(err, buf, sizeof(buf)); libbpf_strerror(err, buf, sizeof(buf));
_LOGW("failed to create CLAT BPF hook: %s", buf); _LOGW("clat: failed to create the BPF hook: %s", buf);
clat_bpf__destroy(self->priv.p->clat_bpf); clat_bpf__destroy(self->priv.p->clat_bpf);
self->priv.p->clat_bpf = NULL; self->priv.p->clat_bpf = NULL;
return; return;
@ -5670,7 +5702,7 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
attach_ingress.prog_fd = bpf_program__fd(self->priv.p->clat_bpf->progs.clat_ingress); attach_ingress.prog_fd = bpf_program__fd(self->priv.p->clat_bpf->progs.clat_ingress);
if (attach_ingress.prog_fd < 0) { if (attach_ingress.prog_fd < 0) {
_LOGW("couldn't find CLAT BPF ingress!"); _LOGW("clat: couldn't find the BPF ingress");
clat_bpf__destroy(self->priv.p->clat_bpf); clat_bpf__destroy(self->priv.p->clat_bpf);
self->priv.p->clat_bpf = NULL; self->priv.p->clat_bpf = NULL;
return; return;
@ -5678,7 +5710,7 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
attach_egress.prog_fd = bpf_program__fd(self->priv.p->clat_bpf->progs.clat_egress); attach_egress.prog_fd = bpf_program__fd(self->priv.p->clat_bpf->progs.clat_egress);
if (attach_egress.prog_fd < 0) { if (attach_egress.prog_fd < 0) {
_LOGW("couldn't find CLAT BPF egress!"); _LOGW("clat: couldn't find the BPF egress");
clat_bpf__destroy(self->priv.p->clat_bpf); clat_bpf__destroy(self->priv.p->clat_bpf);
self->priv.p->clat_bpf = NULL; self->priv.p->clat_bpf = NULL;
return; return;
@ -5688,7 +5720,7 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
err = bpf_tc_attach(&hook, &attach_ingress); err = bpf_tc_attach(&hook, &attach_ingress);
if (err) { if (err) {
libbpf_strerror(err, buf, sizeof(buf)); libbpf_strerror(err, buf, sizeof(buf));
_LOGW("failed to attach ingress to CLAT BPF hook: %s", buf); _LOGW("clat: failed to attach ingress to BPF hook: %s", buf);
clat_bpf__destroy(self->priv.p->clat_bpf); clat_bpf__destroy(self->priv.p->clat_bpf);
self->priv.p->clat_bpf = NULL; self->priv.p->clat_bpf = NULL;
return; return;
@ -5699,7 +5731,7 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
err = bpf_tc_attach(&hook, &attach_egress); err = bpf_tc_attach(&hook, &attach_egress);
if (err) { if (err) {
libbpf_strerror(err, buf, sizeof(buf)); libbpf_strerror(err, buf, sizeof(buf));
_LOGW("failed to attach ingress to CLAT BPF hook: %s", buf); _LOGW("clat: failed to attach ingress to BPF hook: %s", buf);
clat_bpf__destroy(self->priv.p->clat_bpf); clat_bpf__destroy(self->priv.p->clat_bpf);
self->priv.p->clat_bpf = NULL; self->priv.p->clat_bpf = NULL;
return; return;
@ -5708,28 +5740,28 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
} }
_l3_get_pref64_config(self, _l3_get_pref64_config(self,
&v4_key_commited, &v4_key_committed,
&v4_value_commited, &v4_value_committed,
&v6_key_commited, &v6_key_committed,
&v6_value_commited, &v6_value_committed,
TRUE); TRUE);
_l3_get_pref64_config(self, &v4_key_new, &v4_value_new, &v6_key_new, &v6_value_new, FALSE); _l3_get_pref64_config(self, &v4_key_new, &v4_value_new, &v6_key_new, &v6_value_new, FALSE);
if (memcmp(&v4_key_commited, &v4_key_new, sizeof(v4_key_new))) { if (memcmp(&v4_key_committed, &v4_key_new, sizeof(v4_key_new))) {
bpf_map_delete_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v4_config_map), bpf_map_delete_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v4_config_map),
&v4_key_commited); &v4_key_committed);
} }
if (memcmp(&v6_key_commited, &v6_key_new, sizeof(v6_key_new))) { if (memcmp(&v6_key_committed, &v6_key_new, sizeof(v6_key_new))) {
bpf_map_delete_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v6_config_map), bpf_map_delete_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v6_config_map),
&v6_key_commited); &v6_key_committed);
} }
if (memcmp(&v4_value_commited, &v4_value_new, sizeof(v4_value_new))) { if (memcmp(&v4_value_committed, &v4_value_new, sizeof(v4_value_new))) {
bpf_map_update_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v4_config_map), bpf_map_update_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v4_config_map),
&v4_key_new, &v4_key_new,
&v4_value_new, &v4_value_new,
BPF_ANY); BPF_ANY);
} }
if (memcmp(&v6_value_commited, &v6_value_new, sizeof(v6_value_new))) { if (memcmp(&v6_value_committed, &v6_value_new, sizeof(v6_value_new))) {
bpf_map_update_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v6_config_map), bpf_map_update_elem(bpf_map__fd(self->priv.p->clat_bpf->maps.v6_config_map),
&v6_key_new, &v6_key_new,
&v6_value_new, &v6_value_new,
@ -5746,9 +5778,7 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
if (self->priv.p->clat_socket < 0) { if (self->priv.p->clat_socket < 0) {
self->priv.p->clat_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); self->priv.p->clat_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (self->priv.p->clat_socket < 0) { if (self->priv.p->clat_socket < 0) {
_LOGW("Couldn't create CLAT socket for %d! %s", _LOGW("clat: couldn't create the socket: %s", nm_strerror_native(errno));
self->priv.ifindex,
nm_strerror_native(errno));
} }
} }
if (self->priv.p->clat_socket >= 0) { if (self->priv.p->clat_socket >= 0) {
@ -5757,35 +5787,31 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
SO_BINDTOIFINDEX, SO_BINDTOIFINDEX,
&self->priv.ifindex, &self->priv.ifindex,
sizeof(self->priv.ifindex)); sizeof(self->priv.ifindex));
if (err) { if (err < 0) {
_LOGW("Couldn't bind CLAT socket to interface %d! %s", _LOGW("clat: couldn't bind the socket: %s", nm_strerror_native(errno));
self->priv.ifindex,
nm_strerror_native(errno));
} }
} }
v6_changed = v6_changed =
(self->priv.p->clat_address_6_valid != self->priv.p->clat_address_6_commited_valid) (self->priv.p->clat_address_6_valid != self->priv.p->clat_address_6_committed_valid)
|| (self->priv.p->clat_address_6_valid && self->priv.p->clat_address_6_commited_valid || (self->priv.p->clat_address_6_valid && self->priv.p->clat_address_6_committed_valid
&& memcmp(&self->priv.p->clat_address_6.address, && memcmp(&self->priv.p->clat_address_6.address,
&self->priv.p->clat_address_6_commited.address, &self->priv.p->clat_address_6_committed.address,
sizeof(self->priv.p->clat_address_6_commited.address))); sizeof(self->priv.p->clat_address_6_committed.address)));
if (self->priv.p->clat_socket > 0 && v6_changed) { if (self->priv.p->clat_socket > 0 && v6_changed) {
struct ipv6_mreq mreq = {.ipv6mr_interface = self->priv.ifindex}; struct ipv6_mreq mreq = {.ipv6mr_interface = self->priv.ifindex};
if (self->priv.p->clat_address_6_commited_valid) { if (self->priv.p->clat_address_6_committed_valid) {
mreq.ipv6mr_multiaddr = self->priv.p->clat_address_6_commited.address; mreq.ipv6mr_multiaddr = self->priv.p->clat_address_6_committed.address;
err = setsockopt(self->priv.p->clat_socket, err = setsockopt(self->priv.p->clat_socket,
SOL_IPV6, SOL_IPV6,
IPV6_LEAVE_ANYCAST, IPV6_LEAVE_ANYCAST,
&mreq, &mreq,
sizeof(mreq)); sizeof(mreq));
if (err) { if (err < 0) {
_LOGW("Couldn't leave CLAT anycast group on interface %d! %s", _LOGW("clat: couldn't leave the anycast group: %s", nm_strerror_native(errno));
self->priv.ifindex,
nm_strerror_native(errno));
} }
} }
@ -5797,10 +5823,8 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
IPV6_JOIN_ANYCAST, IPV6_JOIN_ANYCAST,
&mreq, &mreq,
sizeof(mreq)); sizeof(mreq));
if (err) { if (err < 0) {
_LOGW("Couldn't join CLAT anycast group on interface %d! %s", _LOGW("clat: couldn't join the anycast group: %s", nm_strerror_native(errno));
self->priv.ifindex,
nm_strerror_native(errno));
} }
} }
} }
@ -5809,27 +5833,27 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
_l3_clat_destroy(self); _l3_clat_destroy(self);
} }
/* Commited will get cleaned up below */ /* Committed will get cleaned up below */
self->priv.p->clat_address_6_valid = FALSE; self->priv.p->clat_address_6_valid = FALSE;
nm_clear_fd(&self->priv.p->clat_socket); nm_clear_fd(&self->priv.p->clat_socket);
/* Deallocate the v4 address. Commited address will get cleaned up below, /* Deallocate the v4 address. Committed address will get cleaned up below,
but we need to make sure there's no double-free */ but we need to make sure there's no double-free */
if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_commited) { if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_committed) {
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_range_release); nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_reservation_release);
} else { } else {
self->priv.p->clat_address_4 = NULL; self->priv.p->clat_address_4 = NULL;
} }
} }
/* Record the new state */ /* Record the new state */
if (self->priv.p->clat_address_4_commited != self->priv.p->clat_address_4) { if (self->priv.p->clat_address_4_committed != self->priv.p->clat_address_4) {
nm_clear_pointer(&self->priv.p->clat_address_4_commited, nm_netns_ip_range_release); nm_clear_pointer(&self->priv.p->clat_address_4_committed, nm_netns_ip_reservation_release);
self->priv.p->clat_address_4_commited = self->priv.p->clat_address_4; self->priv.p->clat_address_4_committed = self->priv.p->clat_address_4;
} }
self->priv.p->clat_address_6_commited = self->priv.p->clat_address_6; self->priv.p->clat_address_6_committed = self->priv.p->clat_address_6;
self->priv.p->clat_address_6_commited_valid = self->priv.p->clat_address_6_valid; self->priv.p->clat_address_6_committed_valid = self->priv.p->clat_address_6_valid;
} }
#endif /* HAVE_CLAT */ #endif /* HAVE_CLAT */
@ -5915,7 +5939,7 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle)
_l3_acd_data_process_changes(self); _l3_acd_data_process_changes(self);
#ifdef HAVE_CLAT #if HAVE_CLAT
_l3_commit_pref64(self, commit_type); _l3_commit_pref64(self, commit_type);
#endif /* HAVE_CLAT */ #endif /* HAVE_CLAT */
@ -6300,7 +6324,9 @@ nm_l3cfg_init(NML3Cfg *self)
{ {
self->priv.p = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_L3CFG, NML3CfgPrivate); self->priv.p = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_L3CFG, NML3CfgPrivate);
#if HAVE_CLAT
self->priv.p->clat_socket = -1; self->priv.p->clat_socket = -1;
#endif /* HAVE_CLAT */
c_list_init(&self->priv.p->acd_lst_head); c_list_init(&self->priv.p->acd_lst_head);
c_list_init(&self->priv.p->acd_event_notify_lst_head); c_list_init(&self->priv.p->acd_event_notify_lst_head);
@ -6356,9 +6382,6 @@ finalize(GObject *object)
{ {
NML3Cfg *self = NM_L3CFG(object); NML3Cfg *self = NM_L3CFG(object);
gboolean changed; gboolean changed;
#ifdef HAVE_CLAT
DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS);
#endif /* HAVE_CLAT */
if (self->priv.netns) { if (self->priv.netns) {
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_INET));
@ -6413,9 +6436,9 @@ finalize(GObject *object)
if (changed) if (changed)
nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, FALSE); nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, FALSE);
#ifdef HAVE_CLAT #if HAVE_CLAT
self->priv.p->clat_address_4_commited = NULL; self->priv.p->clat_address_4_committed = NULL;
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_range_release); nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_reservation_release);
nm_clear_fd(&self->priv.p->clat_socket); nm_clear_fd(&self->priv.p->clat_socket);
if (self->priv.p->clat_bpf) { if (self->priv.p->clat_bpf) {
_l3_clat_destroy(self); _l3_clat_destroy(self);