clat: pass the configuration as struct

The program only needs to know the local IPv4 address, the local IPv6
address and the PREF64. There is no need to create multiple maps for
that, just pass a configuration struct containing those 3 fields.
This commit is contained in:
Beniamino Galvani 2025-10-26 17:44:12 +01:00
parent 195d6e5561
commit cca9e1afce
3 changed files with 44 additions and 162 deletions

View file

@ -34,21 +34,7 @@
char _license[] SEC("license") = "GPL";
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct clat_v4_config_key);
__type(value, struct clat_v4_config_value);
__uint(max_entries, 16);
__uint(map_flags, BPF_F_NO_PREALLOC);
} v4_config_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct clat_v6_config_key);
__type(value, struct clat_v6_config_value);
__uint(max_entries, 16);
__uint(map_flags, BPF_F_NO_PREALLOC);
} v6_config_map SEC(".maps");
struct clat_config config;
#ifdef DEBUG
#define DBG(fmt, ...) \
@ -305,25 +291,15 @@ clat_handle_v4(struct __sk_buff *skb)
struct iphdr *iph;
struct ethhdr *eth;
struct clat_v4_config_value *v4_config;
struct clat_v4_config_key v4_config_key;
iph = data + sizeof(struct ethhdr);
if (iph + 1 > data_end)
goto out;
v4_config_key.ifindex = skb->ifindex;
v4_config_key.local_v4.s_addr = iph->saddr;
v4_config = bpf_map_lookup_elem(&v4_config_map, &v4_config_key);
if (!v4_config) {
DBG("-> v4: config for src_v4=%pI4 not found!\n", &v4_config_key.local_v4);
if (iph->saddr != config.local_v4.s_addr)
goto out;
}
/* At this point we know the destination IP is within the configured
* subnet, so if we can't rewrite the packet it should be dropped (so as
* not to leak traffic in that subnet).
/* At this point we know the packet needs translation. If we can't
* rewrite it, it should be dropped.
*/
ret = TC_ACT_SHOT;
@ -343,8 +319,8 @@ clat_handle_v4(struct __sk_buff *skb)
}
/* src v4 as last octet of clat address */
dst_hdr.saddr = v4_config->local_v6;
dst_hdr.daddr = v4_config->pref64;
dst_hdr.saddr = config.local_v6;
dst_hdr.daddr = config.pref64;
dst_hdr.daddr.s6_addr32[3] = iph->daddr;
dst_hdr.nexthdr = iph->protocol;
dst_hdr.hop_limit = iph->ttl;
@ -499,6 +475,18 @@ rewrite_icmpv6(struct ipv6hdr *ip6h, struct __sk_buff *skb)
return 0;
}
static __always_inline bool
v6addr_equal(const struct in6_addr *a, const struct in6_addr *b)
{
int i;
for (i = 0; i < 4; i++) {
if (a->s6_addr32[i] != b->s6_addr32[i])
return false;
}
return true;
}
/* ipv6 traffic from the PLAT, to be translated into ipv4 and sent to an application */
static int
clat_handle_v6(struct __sk_buff *skb)
@ -506,6 +494,7 @@ clat_handle_v6(struct __sk_buff *skb)
int ret = TC_ACT_OK;
void *data_end = SKB_DATA_END(skb);
void *data = SKB_DATA(skb);
struct in6_addr subnet_v6;
struct ethhdr *eth;
struct iphdr *iph;
struct ipv6hdr *ip6h;
@ -515,30 +504,21 @@ clat_handle_v6(struct __sk_buff *skb)
.frag_off = bpf_htons(1 << 14), /* set Don't Fragment bit */
};
struct clat_v6_config_value *v6_config;
struct clat_v6_config_key v6_config_key;
ip6h = data + sizeof(struct ethhdr);
if (ip6h + 1 > data_end)
goto out;
v6_config_key.local_v6 = ip6h->daddr;
v6_config_key.pref64 = ip6h->saddr;
/* v6 pxlen is always 96 */
v6_config_key.pref64.s6_addr32[3] = 0;
v6_config_key.ifindex = skb->ifindex;
v6_config = bpf_map_lookup_elem(&v6_config_map, &v6_config_key);
if (!v6_config) {
DBG("<- v6: config for pref64=%pI6c, local_v6=%pI6c not found!\n",
&v6_config_key.pref64,
&v6_config_key.local_v6);
if (!v6addr_equal(&ip6h->daddr, &config.local_v6))
goto out;
}
/* At this point we know the destination IP is within the configured
* subnet, so if we can't rewrite the packet it should be dropped (so as
* not to leak traffic in that subnet).
subnet_v6 = ip6h->saddr;
subnet_v6.s6_addr32[3] = 0; /* prefix len is always 96 for now */
if (!v6addr_equal(&subnet_v6, &config.pref64))
goto out;
/* At this point we know the packet needs translation. If we can't
* rewrite it, it should be dropped.
*/
ret = TC_ACT_SHOT;
@ -547,7 +527,7 @@ clat_handle_v6(struct __sk_buff *skb)
&& ip6h->nexthdr != IPPROTO_ICMPV6)
goto out;
dst_hdr.daddr = v6_config->local_v4.s_addr;
dst_hdr.daddr = config.local_v4.s_addr;
dst_hdr.saddr = ip6h->saddr.s6_addr32[3];
dst_hdr.protocol = ip6h->nexthdr;
dst_hdr.ttl = ip6h->hop_limit;

View file

@ -4,24 +4,10 @@
#include <linux/in6.h>
struct clat_v6_config_key {
struct clat_config {
struct in6_addr local_v6;
struct in6_addr pref64;
__u32 ifindex;
};
struct clat_v6_config_value {
struct in_addr local_v4;
};
struct clat_v4_config_key {
struct in_addr local_v4;
__u32 ifindex;
};
struct clat_v4_config_value {
struct in6_addr local_v6;
struct in6_addr pref64;
};
#endif

View file

@ -310,9 +310,6 @@ typedef struct _NML3CfgPrivate {
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 */
struct clat_bpf *clat_bpf;
int clat_socket;
@ -387,7 +384,6 @@ typedef struct _NML3CfgPrivate {
bool clat_address_6_valid : 1;
bool clat_address_6_committed_valid : 1;
bool clat_pref64_valid : 1;
} NML3CfgPrivate;
struct _NML3CfgClass {
@ -5564,50 +5560,6 @@ _l3_commit_one(NML3Cfg *self,
}
#if HAVE_CLAT
static void
_l3_get_pref64_config(NML3Cfg *self,
struct clat_v4_config_key *v4_key,
struct clat_v4_config_value *v4_value,
struct clat_v6_config_key *v6_key,
struct clat_v6_config_value *v6_value,
gboolean committed)
{
const NML3ConfigData *l3cd = self->priv.p->combined_l3cd_commited;
struct in6_addr pref64;
guint32 pref64_plen;
memset(v4_key, 0, sizeof(*v4_key));
memset(v6_key, 0, sizeof(*v6_key));
memset(v4_value, 0, sizeof(*v4_value));
memset(v6_value, 0, sizeof(*v6_value));
v4_key->ifindex = v6_key->ifindex = self->priv.ifindex;
if (committed) {
if (self->priv.p->clat_address_4_committed) {
v6_value->local_v4.s_addr = v4_key->local_v4.s_addr =
self->priv.p->clat_address_4_committed->addr;
}
if (self->priv.p->clat_address_6_committed_valid) {
v4_value->local_v6 = v6_key->local_v6 = self->priv.p->clat_address_6_committed.address;
}
if (self->priv.p->clat_pref64_valid) {
v6_key->pref64 = v4_value->pref64 = self->priv.p->clat_pref64;
}
} else {
if (self->priv.p->clat_address_4) {
v6_value->local_v4.s_addr = v4_key->local_v4.s_addr =
self->priv.p->clat_address_4->addr;
}
if (self->priv.p->clat_address_6_valid) {
v4_value->local_v6 = v6_key->local_v6 = self->priv.p->clat_address_6.address;
}
if (nm_l3_config_data_get_pref64(l3cd, &pref64, &pref64_plen)) {
v6_key->pref64 = v4_value->pref64 = pref64;
}
}
}
static void
_l3_clat_destroy(NML3Cfg *self)
{
@ -5643,17 +5595,14 @@ _l3_clat_destroy(NML3Cfg *self)
static void
_l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
{
int err = 0;
const NML3ConfigData *l3cd = self->priv.p->combined_l3cd_commited;
struct in6_addr _l3cd_pref64_inner;
const struct in6_addr *l3cd_pref64 = NULL;
guint32 l3cd_pref64_plen;
char buf[100];
struct clat_v6_config_key v6_key_committed, v6_key_new;
struct clat_v4_config_key v4_key_committed, v4_key_new;
struct clat_v6_config_value v6_value_committed, v6_value_new;
struct clat_v4_config_value v4_value_committed, v4_value_new;
gboolean v6_changed;
int err = 0;
const NML3ConfigData *l3cd = self->priv.p->combined_l3cd_commited;
struct in6_addr _l3cd_pref64_inner;
const struct in6_addr *l3cd_pref64 = NULL;
guint32 l3cd_pref64_plen;
char buf[100];
struct clat_config clat_config;
gboolean v6_changed;
DECLARE_LIBBPF_OPTS(bpf_tc_hook,
hook,
.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS,
@ -5678,10 +5627,6 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
return;
}
/* Set up maps */
bpf_map__set_max_entries(self->priv.p->clat_bpf->maps.v4_config_map, 16);
bpf_map__set_max_entries(self->priv.p->clat_bpf->maps.v6_config_map, 16);
err = clat_bpf__load(self->priv.p->clat_bpf);
if (err) {
libbpf_strerror(err, buf, sizeof(buf));
@ -5739,41 +5684,12 @@ _l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
self->priv.p->clat_attach_egress = attach_egress;
}
_l3_get_pref64_config(self,
&v4_key_committed,
&v4_value_committed,
&v6_key_committed,
&v6_value_committed,
TRUE);
_l3_get_pref64_config(self, &v4_key_new, &v4_value_new, &v6_key_new, &v6_value_new, FALSE);
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),
&v4_key_committed);
}
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),
&v6_key_committed);
}
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),
&v4_key_new,
&v4_value_new,
BPF_ANY);
}
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),
&v6_key_new,
&v6_value_new,
BPF_ANY);
}
if (l3cd_pref64) {
self->priv.p->clat_pref64_valid = TRUE;
self->priv.p->clat_pref64 = *l3cd_pref64;
} else {
self->priv.p->clat_pref64_valid = FALSE;
}
/* Pass configuration to the BPF program */
memset(&clat_config, 0, sizeof(clat_config));
clat_config.local_v4.s_addr = self->priv.p->clat_address_4->addr;
clat_config.local_v6 = self->priv.p->clat_address_6.address;
clat_config.pref64 = *l3cd_pref64;
self->priv.p->clat_bpf->bss->config = clat_config;
if (self->priv.p->clat_socket < 0) {
self->priv.p->clat_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);