Skip to content

Commit 94151f5

Browse files
ebirgerMartin KaFai Lau
authored andcommitted
xfrm: interface: Add unstable helpers for setting/getting XFRM metadata from TC-BPF
This change adds xfrm metadata helpers using the unstable kfunc call interface for the TC-BPF hooks. This allows steering traffic towards different IPsec connections based on logic implemented in bpf programs. This object is built based on the availability of BTF debug info. When setting the xfrm metadata, percpu metadata dsts are used in order to avoid allocating a metadata dst per packet. In order to guarantee safe module unload, the percpu dsts are allocated on first use and never freed. The percpu pointer is stored in net/core/filter.c so that it can be reused on module reload. The metadata percpu dsts take ownership of the original skb dsts so that they may be used as part of the xfrm transmission logic - e.g. for MTU calculations. Signed-off-by: Eyal Birger <eyal.birger@gmail.com> Link: https://lore.kernel.org/r/20221203084659.1837829-3-eyal.birger@gmail.com Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
1 parent ee9a113 commit 94151f5

File tree

7 files changed

+168
-2
lines changed

7 files changed

+168
-2
lines changed

include/net/dst_metadata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct macsec_info {
2626
struct xfrm_md_info {
2727
u32 if_id;
2828
int link;
29+
struct dst_entry *dst_orig;
2930
};
3031

3132
struct metadata_dst {

include/net/xfrm.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,4 +2086,21 @@ static inline bool xfrm6_local_dontfrag(const struct sock *sk)
20862086
return false;
20872087
}
20882088
#endif
2089+
2090+
#if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \
2091+
(IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES))
2092+
2093+
extern struct metadata_dst __percpu *xfrm_bpf_md_dst;
2094+
2095+
int register_xfrm_interface_bpf(void);
2096+
2097+
#else
2098+
2099+
static inline int register_xfrm_interface_bpf(void)
2100+
{
2101+
return 0;
2102+
}
2103+
2104+
#endif
2105+
20892106
#endif /* _NET_XFRM_H */

net/core/dst.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ void metadata_dst_free(struct metadata_dst *md_dst)
316316
if (md_dst->type == METADATA_IP_TUNNEL)
317317
dst_cache_destroy(&md_dst->u.tun_info.dst_cache);
318318
#endif
319+
if (md_dst->type == METADATA_XFRM)
320+
dst_release(md_dst->u.xfrm_info.dst_orig);
319321
kfree(md_dst);
320322
}
321323
EXPORT_SYMBOL_GPL(metadata_dst_free);
@@ -340,16 +342,18 @@ EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu);
340342

341343
void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst)
342344
{
343-
#ifdef CONFIG_DST_CACHE
344345
int cpu;
345346

346347
for_each_possible_cpu(cpu) {
347348
struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu);
348349

350+
#ifdef CONFIG_DST_CACHE
349351
if (one_md_dst->type == METADATA_IP_TUNNEL)
350352
dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache);
351-
}
352353
#endif
354+
if (one_md_dst->type == METADATA_XFRM)
355+
dst_release(one_md_dst->u.xfrm_info.dst_orig);
356+
}
353357
free_percpu(md_dst);
354358
}
355359
EXPORT_SYMBOL_GPL(metadata_dst_free_percpu);

net/core/filter.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5631,6 +5631,15 @@ static const struct bpf_func_proto bpf_bind_proto = {
56315631
};
56325632

56335633
#ifdef CONFIG_XFRM
5634+
5635+
#if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \
5636+
(IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES))
5637+
5638+
struct metadata_dst __percpu *xfrm_bpf_md_dst;
5639+
EXPORT_SYMBOL_GPL(xfrm_bpf_md_dst);
5640+
5641+
#endif
5642+
56345643
BPF_CALL_5(bpf_skb_get_xfrm_state, struct sk_buff *, skb, u32, index,
56355644
struct bpf_xfrm_state *, to, u32, size, u64, flags)
56365645
{

net/xfrm/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55

66
xfrm_interface-$(CONFIG_XFRM_INTERFACE) += xfrm_interface_core.o
77

8+
ifeq ($(CONFIG_XFRM_INTERFACE),m)
9+
xfrm_interface-$(CONFIG_DEBUG_INFO_BTF_MODULES) += xfrm_interface_bpf.o
10+
else ifeq ($(CONFIG_XFRM_INTERFACE),y)
11+
xfrm_interface-$(CONFIG_DEBUG_INFO_BTF) += xfrm_interface_bpf.o
12+
endif
13+
814
obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
915
xfrm_input.o xfrm_output.o \
1016
xfrm_sysctl.o xfrm_replay.o xfrm_device.o

net/xfrm/xfrm_interface_bpf.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Unstable XFRM Helpers for TC-BPF hook
3+
*
4+
* These are called from SCHED_CLS BPF programs. Note that it is
5+
* allowed to break compatibility for these functions since the interface they
6+
* are exposed through to BPF programs is explicitly unstable.
7+
*/
8+
9+
#include <linux/bpf.h>
10+
#include <linux/btf_ids.h>
11+
12+
#include <net/dst_metadata.h>
13+
#include <net/xfrm.h>
14+
15+
/* bpf_xfrm_info - XFRM metadata information
16+
*
17+
* Members:
18+
* @if_id - XFRM if_id:
19+
* Transmit: if_id to be used in policy and state lookups
20+
* Receive: if_id of the state matched for the incoming packet
21+
* @link - Underlying device ifindex:
22+
* Transmit: used as the underlying device in VRF routing
23+
* Receive: the device on which the packet had been received
24+
*/
25+
struct bpf_xfrm_info {
26+
u32 if_id;
27+
int link;
28+
};
29+
30+
__diag_push();
31+
__diag_ignore_all("-Wmissing-prototypes",
32+
"Global functions as their definitions will be in xfrm_interface BTF");
33+
34+
/* bpf_skb_get_xfrm_info - Get XFRM metadata
35+
*
36+
* Parameters:
37+
* @skb_ctx - Pointer to ctx (__sk_buff) in TC program
38+
* Cannot be NULL
39+
* @to - Pointer to memory to which the metadata will be copied
40+
* Cannot be NULL
41+
*/
42+
__used noinline
43+
int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx, struct bpf_xfrm_info *to)
44+
{
45+
struct sk_buff *skb = (struct sk_buff *)skb_ctx;
46+
struct xfrm_md_info *info;
47+
48+
info = skb_xfrm_md_info(skb);
49+
if (!info)
50+
return -EINVAL;
51+
52+
to->if_id = info->if_id;
53+
to->link = info->link;
54+
return 0;
55+
}
56+
57+
/* bpf_skb_get_xfrm_info - Set XFRM metadata
58+
*
59+
* Parameters:
60+
* @skb_ctx - Pointer to ctx (__sk_buff) in TC program
61+
* Cannot be NULL
62+
* @from - Pointer to memory from which the metadata will be copied
63+
* Cannot be NULL
64+
*/
65+
__used noinline
66+
int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx,
67+
const struct bpf_xfrm_info *from)
68+
{
69+
struct sk_buff *skb = (struct sk_buff *)skb_ctx;
70+
struct metadata_dst *md_dst;
71+
struct xfrm_md_info *info;
72+
73+
if (unlikely(skb_metadata_dst(skb)))
74+
return -EINVAL;
75+
76+
if (!xfrm_bpf_md_dst) {
77+
struct metadata_dst __percpu *tmp;
78+
79+
tmp = metadata_dst_alloc_percpu(0, METADATA_XFRM, GFP_ATOMIC);
80+
if (!tmp)
81+
return -ENOMEM;
82+
if (cmpxchg(&xfrm_bpf_md_dst, NULL, tmp))
83+
metadata_dst_free_percpu(tmp);
84+
}
85+
md_dst = this_cpu_ptr(xfrm_bpf_md_dst);
86+
87+
info = &md_dst->u.xfrm_info;
88+
89+
info->if_id = from->if_id;
90+
info->link = from->link;
91+
skb_dst_force(skb);
92+
info->dst_orig = skb_dst(skb);
93+
94+
dst_hold((struct dst_entry *)md_dst);
95+
skb_dst_set(skb, (struct dst_entry *)md_dst);
96+
return 0;
97+
}
98+
99+
__diag_pop()
100+
101+
BTF_SET8_START(xfrm_ifc_kfunc_set)
102+
BTF_ID_FLAGS(func, bpf_skb_get_xfrm_info)
103+
BTF_ID_FLAGS(func, bpf_skb_set_xfrm_info)
104+
BTF_SET8_END(xfrm_ifc_kfunc_set)
105+
106+
static const struct btf_kfunc_id_set xfrm_interface_kfunc_set = {
107+
.owner = THIS_MODULE,
108+
.set = &xfrm_ifc_kfunc_set,
109+
};
110+
111+
int __init register_xfrm_interface_bpf(void)
112+
{
113+
return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS,
114+
&xfrm_interface_kfunc_set);
115+
}

net/xfrm/xfrm_interface_core.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,14 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
396396

397397
if_id = md_info->if_id;
398398
fl->flowi_oif = md_info->link;
399+
if (md_info->dst_orig) {
400+
struct dst_entry *tmp_dst = dst;
401+
402+
dst = md_info->dst_orig;
403+
skb_dst_set(skb, dst);
404+
md_info->dst_orig = NULL;
405+
dst_release(tmp_dst);
406+
}
399407
} else {
400408
if_id = xi->p.if_id;
401409
}
@@ -1162,12 +1170,18 @@ static int __init xfrmi_init(void)
11621170
if (err < 0)
11631171
goto rtnl_link_failed;
11641172

1173+
err = register_xfrm_interface_bpf();
1174+
if (err < 0)
1175+
goto kfunc_failed;
1176+
11651177
lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM);
11661178

11671179
xfrm_if_register_cb(&xfrm_if_cb);
11681180

11691181
return err;
11701182

1183+
kfunc_failed:
1184+
rtnl_link_unregister(&xfrmi_link_ops);
11711185
rtnl_link_failed:
11721186
xfrmi6_fini();
11731187
xfrmi6_failed:

0 commit comments

Comments
 (0)