Commit 08388efe authored by Martin KaFai Lau's avatar Martin KaFai Lau

Merge branch 'xfrm: interface: Add unstable helpers for XFRM metadata'

Eyal Birger says:

====================

This patch series 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.

The helpers are integrated into the xfrm_interface module. For this
purpose the main functionality of this module is moved to
xfrm_interface_core.c.
---

changes in v6: fix sparse warning in patch 2
changes in v5:
  - avoid cleanup of percpu dsts as detailed in patch 2
changes in v3:
  - tag bpf-next tree instead of ipsec-next
  - add IFLA_XFRM_COLLECT_METADATA sync patch
====================
Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parents ab0350c7 90a3a05e
...@@ -26,6 +26,7 @@ struct macsec_info { ...@@ -26,6 +26,7 @@ struct macsec_info {
struct xfrm_md_info { struct xfrm_md_info {
u32 if_id; u32 if_id;
int link; int link;
struct dst_entry *dst_orig;
}; };
struct metadata_dst { struct metadata_dst {
......
...@@ -2086,4 +2086,21 @@ static inline bool xfrm6_local_dontfrag(const struct sock *sk) ...@@ -2086,4 +2086,21 @@ static inline bool xfrm6_local_dontfrag(const struct sock *sk)
return false; return false;
} }
#endif #endif
#if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \
(IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES))
extern struct metadata_dst __percpu *xfrm_bpf_md_dst;
int register_xfrm_interface_bpf(void);
#else
static inline int register_xfrm_interface_bpf(void)
{
return 0;
}
#endif
#endif /* _NET_XFRM_H */ #endif /* _NET_XFRM_H */
...@@ -316,6 +316,8 @@ void metadata_dst_free(struct metadata_dst *md_dst) ...@@ -316,6 +316,8 @@ void metadata_dst_free(struct metadata_dst *md_dst)
if (md_dst->type == METADATA_IP_TUNNEL) if (md_dst->type == METADATA_IP_TUNNEL)
dst_cache_destroy(&md_dst->u.tun_info.dst_cache); dst_cache_destroy(&md_dst->u.tun_info.dst_cache);
#endif #endif
if (md_dst->type == METADATA_XFRM)
dst_release(md_dst->u.xfrm_info.dst_orig);
kfree(md_dst); kfree(md_dst);
} }
EXPORT_SYMBOL_GPL(metadata_dst_free); EXPORT_SYMBOL_GPL(metadata_dst_free);
...@@ -340,16 +342,18 @@ EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu); ...@@ -340,16 +342,18 @@ EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu);
void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst) void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst)
{ {
#ifdef CONFIG_DST_CACHE
int cpu; int cpu;
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu); struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu);
#ifdef CONFIG_DST_CACHE
if (one_md_dst->type == METADATA_IP_TUNNEL) if (one_md_dst->type == METADATA_IP_TUNNEL)
dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache); dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache);
}
#endif #endif
if (one_md_dst->type == METADATA_XFRM)
dst_release(one_md_dst->u.xfrm_info.dst_orig);
}
free_percpu(md_dst); free_percpu(md_dst);
} }
EXPORT_SYMBOL_GPL(metadata_dst_free_percpu); EXPORT_SYMBOL_GPL(metadata_dst_free_percpu);
...@@ -5631,6 +5631,15 @@ static const struct bpf_func_proto bpf_bind_proto = { ...@@ -5631,6 +5631,15 @@ static const struct bpf_func_proto bpf_bind_proto = {
}; };
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
#if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \
(IS_MODULE(CONFIG_XFRM_INTERFACE) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES))
struct metadata_dst __percpu *xfrm_bpf_md_dst;
EXPORT_SYMBOL_GPL(xfrm_bpf_md_dst);
#endif
BPF_CALL_5(bpf_skb_get_xfrm_state, struct sk_buff *, skb, u32, index, BPF_CALL_5(bpf_skb_get_xfrm_state, struct sk_buff *, skb, u32, index,
struct bpf_xfrm_state *, to, u32, size, u64, flags) struct bpf_xfrm_state *, to, u32, size, u64, flags)
{ {
......
...@@ -3,6 +3,14 @@ ...@@ -3,6 +3,14 @@
# Makefile for the XFRM subsystem. # Makefile for the XFRM subsystem.
# #
xfrm_interface-$(CONFIG_XFRM_INTERFACE) += xfrm_interface_core.o
ifeq ($(CONFIG_XFRM_INTERFACE),m)
xfrm_interface-$(CONFIG_DEBUG_INFO_BTF_MODULES) += xfrm_interface_bpf.o
else ifeq ($(CONFIG_XFRM_INTERFACE),y)
xfrm_interface-$(CONFIG_DEBUG_INFO_BTF) += xfrm_interface_bpf.o
endif
obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
xfrm_input.o xfrm_output.o \ xfrm_input.o xfrm_output.o \
xfrm_sysctl.o xfrm_replay.o xfrm_device.o xfrm_sysctl.o xfrm_replay.o xfrm_device.o
......
// SPDX-License-Identifier: GPL-2.0-only
/* Unstable XFRM Helpers for TC-BPF hook
*
* These are called from SCHED_CLS BPF programs. Note that it is
* allowed to break compatibility for these functions since the interface they
* are exposed through to BPF programs is explicitly unstable.
*/
#include <linux/bpf.h>
#include <linux/btf_ids.h>
#include <net/dst_metadata.h>
#include <net/xfrm.h>
/* bpf_xfrm_info - XFRM metadata information
*
* Members:
* @if_id - XFRM if_id:
* Transmit: if_id to be used in policy and state lookups
* Receive: if_id of the state matched for the incoming packet
* @link - Underlying device ifindex:
* Transmit: used as the underlying device in VRF routing
* Receive: the device on which the packet had been received
*/
struct bpf_xfrm_info {
u32 if_id;
int link;
};
__diag_push();
__diag_ignore_all("-Wmissing-prototypes",
"Global functions as their definitions will be in xfrm_interface BTF");
/* bpf_skb_get_xfrm_info - Get XFRM metadata
*
* Parameters:
* @skb_ctx - Pointer to ctx (__sk_buff) in TC program
* Cannot be NULL
* @to - Pointer to memory to which the metadata will be copied
* Cannot be NULL
*/
__used noinline
int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx, struct bpf_xfrm_info *to)
{
struct sk_buff *skb = (struct sk_buff *)skb_ctx;
struct xfrm_md_info *info;
info = skb_xfrm_md_info(skb);
if (!info)
return -EINVAL;
to->if_id = info->if_id;
to->link = info->link;
return 0;
}
/* bpf_skb_get_xfrm_info - Set XFRM metadata
*
* Parameters:
* @skb_ctx - Pointer to ctx (__sk_buff) in TC program
* Cannot be NULL
* @from - Pointer to memory from which the metadata will be copied
* Cannot be NULL
*/
__used noinline
int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx,
const struct bpf_xfrm_info *from)
{
struct sk_buff *skb = (struct sk_buff *)skb_ctx;
struct metadata_dst *md_dst;
struct xfrm_md_info *info;
if (unlikely(skb_metadata_dst(skb)))
return -EINVAL;
if (!xfrm_bpf_md_dst) {
struct metadata_dst __percpu *tmp;
tmp = metadata_dst_alloc_percpu(0, METADATA_XFRM, GFP_ATOMIC);
if (!tmp)
return -ENOMEM;
if (cmpxchg(&xfrm_bpf_md_dst, NULL, tmp))
metadata_dst_free_percpu(tmp);
}
md_dst = this_cpu_ptr(xfrm_bpf_md_dst);
info = &md_dst->u.xfrm_info;
info->if_id = from->if_id;
info->link = from->link;
skb_dst_force(skb);
info->dst_orig = skb_dst(skb);
dst_hold((struct dst_entry *)md_dst);
skb_dst_set(skb, (struct dst_entry *)md_dst);
return 0;
}
__diag_pop()
BTF_SET8_START(xfrm_ifc_kfunc_set)
BTF_ID_FLAGS(func, bpf_skb_get_xfrm_info)
BTF_ID_FLAGS(func, bpf_skb_set_xfrm_info)
BTF_SET8_END(xfrm_ifc_kfunc_set)
static const struct btf_kfunc_id_set xfrm_interface_kfunc_set = {
.owner = THIS_MODULE,
.set = &xfrm_ifc_kfunc_set,
};
int __init register_xfrm_interface_bpf(void)
{
return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS,
&xfrm_interface_kfunc_set);
}
...@@ -396,6 +396,14 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) ...@@ -396,6 +396,14 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
if_id = md_info->if_id; if_id = md_info->if_id;
fl->flowi_oif = md_info->link; fl->flowi_oif = md_info->link;
if (md_info->dst_orig) {
struct dst_entry *tmp_dst = dst;
dst = md_info->dst_orig;
skb_dst_set(skb, dst);
md_info->dst_orig = NULL;
dst_release(tmp_dst);
}
} else { } else {
if_id = xi->p.if_id; if_id = xi->p.if_id;
} }
...@@ -1162,12 +1170,18 @@ static int __init xfrmi_init(void) ...@@ -1162,12 +1170,18 @@ static int __init xfrmi_init(void)
if (err < 0) if (err < 0)
goto rtnl_link_failed; goto rtnl_link_failed;
err = register_xfrm_interface_bpf();
if (err < 0)
goto kfunc_failed;
lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM);
xfrm_if_register_cb(&xfrm_if_cb); xfrm_if_register_cb(&xfrm_if_cb);
return err; return err;
kfunc_failed:
rtnl_link_unregister(&xfrmi_link_ops);
rtnl_link_failed: rtnl_link_failed:
xfrmi6_fini(); xfrmi6_fini();
xfrmi6_failed: xfrmi6_failed:
......
...@@ -673,6 +673,7 @@ enum { ...@@ -673,6 +673,7 @@ enum {
IFLA_XFRM_UNSPEC, IFLA_XFRM_UNSPEC,
IFLA_XFRM_LINK, IFLA_XFRM_LINK,
IFLA_XFRM_IF_ID, IFLA_XFRM_IF_ID,
IFLA_XFRM_COLLECT_METADATA,
__IFLA_XFRM_MAX __IFLA_XFRM_MAX
}; };
......
...@@ -85,3 +85,4 @@ xdp_bonding # failed to auto-attach program 'trace_ ...@@ -85,3 +85,4 @@ xdp_bonding # failed to auto-attach program 'trace_
xdp_bpf2bpf # failed to auto-attach program 'trace_on_entry': -524 (trampoline) xdp_bpf2bpf # failed to auto-attach program 'trace_on_entry': -524 (trampoline)
xdp_do_redirect # prog_run_max_size unexpected error: -22 (errno 22) xdp_do_redirect # prog_run_max_size unexpected error: -22 (errno 22)
xdp_synproxy # JIT does not support calling kernel function (kfunc) xdp_synproxy # JIT does not support calling kernel function (kfunc)
xfrm_info # JIT does not support calling kernel function (kfunc)
...@@ -23,6 +23,7 @@ CONFIG_IKCONFIG_PROC=y ...@@ -23,6 +23,7 @@ CONFIG_IKCONFIG_PROC=y
CONFIG_IMA=y CONFIG_IMA=y
CONFIG_IMA_READ_POLICY=y CONFIG_IMA_READ_POLICY=y
CONFIG_IMA_WRITE_POLICY=y CONFIG_IMA_WRITE_POLICY=y
CONFIG_INET_ESP=y
CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_RAW=y CONFIG_IP_NF_RAW=y
CONFIG_IP_NF_TARGET_SYNPROXY=y CONFIG_IP_NF_TARGET_SYNPROXY=y
...@@ -74,3 +75,4 @@ CONFIG_TEST_BPF=y ...@@ -74,3 +75,4 @@ CONFIG_TEST_BPF=y
CONFIG_USERFAULTFD=y CONFIG_USERFAULTFD=y
CONFIG_VXLAN=y CONFIG_VXLAN=y
CONFIG_XDP_SOCKETS=y CONFIG_XDP_SOCKETS=y
CONFIG_XFRM_INTERFACE=y
This diff is collapsed.
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
#define IPV6_TCLASS 67 #define IPV6_TCLASS 67
#define IPV6_AUTOFLOWLABEL 70 #define IPV6_AUTOFLOWLABEL 70
#define TC_ACT_UNSPEC (-1)
#define TC_ACT_SHOT 2
#define SOL_TCP 6 #define SOL_TCP 6
#define TCP_NODELAY 1 #define TCP_NODELAY 1
#define TCP_MAXSEG 2 #define TCP_MAXSEG 2
......
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include "bpf_tracing_net.h"
#include <bpf/bpf_helpers.h>
__u32 req_if_id;
__u32 resp_if_id;
int bpf_skb_set_xfrm_info(struct __sk_buff *skb_ctx,
const struct bpf_xfrm_info *from) __ksym;
int bpf_skb_get_xfrm_info(struct __sk_buff *skb_ctx,
struct bpf_xfrm_info *to) __ksym;
SEC("tc")
int set_xfrm_info(struct __sk_buff *skb)
{
struct bpf_xfrm_info info = { .if_id = req_if_id };
return bpf_skb_set_xfrm_info(skb, &info) ? TC_ACT_SHOT : TC_ACT_UNSPEC;
}
SEC("tc")
int get_xfrm_info(struct __sk_buff *skb)
{
struct bpf_xfrm_info info = {};
if (bpf_skb_get_xfrm_info(skb, &info) < 0)
return TC_ACT_SHOT;
resp_if_id = info.if_id;
return TC_ACT_UNSPEC;
}
char _license[] SEC("license") = "GPL";
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment