mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 13:53:33 +00:00
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: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
commit
08388efe59
13 changed files with 574 additions and 2 deletions
|
@ -26,6 +26,7 @@ struct macsec_info {
|
|||
struct xfrm_md_info {
|
||||
u32 if_id;
|
||||
int link;
|
||||
struct dst_entry *dst_orig;
|
||||
};
|
||||
|
||||
struct metadata_dst {
|
||||
|
|
|
@ -2086,4 +2086,21 @@ static inline bool xfrm6_local_dontfrag(const struct sock *sk)
|
|||
return false;
|
||||
}
|
||||
#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 */
|
||||
|
|
|
@ -316,6 +316,8 @@ void metadata_dst_free(struct metadata_dst *md_dst)
|
|||
if (md_dst->type == METADATA_IP_TUNNEL)
|
||||
dst_cache_destroy(&md_dst->u.tun_info.dst_cache);
|
||||
#endif
|
||||
if (md_dst->type == METADATA_XFRM)
|
||||
dst_release(md_dst->u.xfrm_info.dst_orig);
|
||||
kfree(md_dst);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(metadata_dst_free);
|
||||
|
@ -340,16 +342,18 @@ EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu);
|
|||
|
||||
void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst)
|
||||
{
|
||||
#ifdef CONFIG_DST_CACHE
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(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)
|
||||
dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache);
|
||||
}
|
||||
#endif
|
||||
if (one_md_dst->type == METADATA_XFRM)
|
||||
dst_release(one_md_dst->u.xfrm_info.dst_orig);
|
||||
}
|
||||
free_percpu(md_dst);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(metadata_dst_free_percpu);
|
||||
|
|
|
@ -5631,6 +5631,15 @@ static const struct bpf_func_proto bpf_bind_proto = {
|
|||
};
|
||||
|
||||
#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,
|
||||
struct bpf_xfrm_state *, to, u32, size, u64, flags)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,14 @@
|
|||
# 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 \
|
||||
xfrm_input.o xfrm_output.o \
|
||||
xfrm_sysctl.o xfrm_replay.o xfrm_device.o
|
||||
|
|
115
net/xfrm/xfrm_interface_bpf.c
Normal file
115
net/xfrm/xfrm_interface_bpf.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
// 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)
|
|||
|
||||
if_id = md_info->if_id;
|
||||
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 {
|
||||
if_id = xi->p.if_id;
|
||||
}
|
||||
|
@ -1162,12 +1170,18 @@ static int __init xfrmi_init(void)
|
|||
if (err < 0)
|
||||
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);
|
||||
|
||||
xfrm_if_register_cb(&xfrm_if_cb);
|
||||
|
||||
return err;
|
||||
|
||||
kfunc_failed:
|
||||
rtnl_link_unregister(&xfrmi_link_ops);
|
||||
rtnl_link_failed:
|
||||
xfrmi6_fini();
|
||||
xfrmi6_failed:
|
|
@ -673,6 +673,7 @@ enum {
|
|||
IFLA_XFRM_UNSPEC,
|
||||
IFLA_XFRM_LINK,
|
||||
IFLA_XFRM_IF_ID,
|
||||
IFLA_XFRM_COLLECT_METADATA,
|
||||
__IFLA_XFRM_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -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_do_redirect # prog_run_max_size unexpected error: -22 (errno 22)
|
||||
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
|
|||
CONFIG_IMA=y
|
||||
CONFIG_IMA_READ_POLICY=y
|
||||
CONFIG_IMA_WRITE_POLICY=y
|
||||
CONFIG_INET_ESP=y
|
||||
CONFIG_IP_NF_FILTER=y
|
||||
CONFIG_IP_NF_RAW=y
|
||||
CONFIG_IP_NF_TARGET_SYNPROXY=y
|
||||
|
@ -74,3 +75,4 @@ CONFIG_TEST_BPF=y
|
|||
CONFIG_USERFAULTFD=y
|
||||
CONFIG_VXLAN=y
|
||||
CONFIG_XDP_SOCKETS=y
|
||||
CONFIG_XFRM_INTERFACE=y
|
||||
|
|
362
tools/testing/selftests/bpf/prog_tests/xfrm_info.c
Normal file
362
tools/testing/selftests/bpf/prog_tests/xfrm_info.c
Normal file
|
@ -0,0 +1,362 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
|
||||
/*
|
||||
* Topology:
|
||||
* ---------
|
||||
* NS0 namespace | NS1 namespace | NS2 namespace
|
||||
* | |
|
||||
* +---------------+ | +---------------+ |
|
||||
* | ipsec0 |---------| ipsec0 | |
|
||||
* | 192.168.1.100 | | | 192.168.1.200 | |
|
||||
* | if_id: bpf | | +---------------+ |
|
||||
* +---------------+ | |
|
||||
* | | | +---------------+
|
||||
* | | | | ipsec0 |
|
||||
* \------------------------------------------| 192.168.1.200 |
|
||||
* | | +---------------+
|
||||
* | |
|
||||
* | | (overlay network)
|
||||
* ------------------------------------------------------
|
||||
* | | (underlay network)
|
||||
* +--------------+ | +--------------+ |
|
||||
* | veth01 |----------| veth10 | |
|
||||
* | 172.16.1.100 | | | 172.16.1.200 | |
|
||||
* ---------------+ | +--------------+ |
|
||||
* | |
|
||||
* +--------------+ | | +--------------+
|
||||
* | veth02 |-----------------------------------| veth20 |
|
||||
* | 172.16.2.100 | | | | 172.16.2.200 |
|
||||
* +--------------+ | | +--------------+
|
||||
*
|
||||
*
|
||||
* Test Packet flow
|
||||
* -----------
|
||||
* The tests perform 'ping 192.168.1.200' from the NS0 namespace:
|
||||
* 1) request is routed to NS0 ipsec0
|
||||
* 2) NS0 ipsec0 tc egress BPF program is triggered and sets the if_id based
|
||||
* on the requested value. This makes the ipsec0 device in external mode
|
||||
* select the destination tunnel
|
||||
* 3) ping reaches the other namespace (NS1 or NS2 based on which if_id was
|
||||
* used) and response is sent
|
||||
* 4) response is received on NS0 ipsec0, tc ingress program is triggered and
|
||||
* records the response if_id
|
||||
* 5) requested if_id is compared with received if_id
|
||||
*/
|
||||
|
||||
#include <net/if.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/if_link.h>
|
||||
|
||||
#include "test_progs.h"
|
||||
#include "network_helpers.h"
|
||||
#include "xfrm_info.skel.h"
|
||||
|
||||
#define NS0 "xfrm_test_ns0"
|
||||
#define NS1 "xfrm_test_ns1"
|
||||
#define NS2 "xfrm_test_ns2"
|
||||
|
||||
#define IF_ID_0_TO_1 1
|
||||
#define IF_ID_0_TO_2 2
|
||||
#define IF_ID_1 3
|
||||
#define IF_ID_2 4
|
||||
|
||||
#define IP4_ADDR_VETH01 "172.16.1.100"
|
||||
#define IP4_ADDR_VETH10 "172.16.1.200"
|
||||
#define IP4_ADDR_VETH02 "172.16.2.100"
|
||||
#define IP4_ADDR_VETH20 "172.16.2.200"
|
||||
|
||||
#define ESP_DUMMY_PARAMS \
|
||||
"proto esp aead 'rfc4106(gcm(aes))' " \
|
||||
"0xe4d8f4b4da1df18a3510b3781496daa82488b713 128 mode tunnel "
|
||||
|
||||
#define SYS(fmt, ...) \
|
||||
({ \
|
||||
char cmd[1024]; \
|
||||
snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
|
||||
if (!ASSERT_OK(system(cmd), cmd)) \
|
||||
goto fail; \
|
||||
})
|
||||
|
||||
#define SYS_NOFAIL(fmt, ...) \
|
||||
({ \
|
||||
char cmd[1024]; \
|
||||
snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
|
||||
system(cmd); \
|
||||
})
|
||||
|
||||
static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, .priority = 1,
|
||||
.prog_fd = igr_fd);
|
||||
LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, .priority = 1,
|
||||
.prog_fd = egr_fd);
|
||||
int ret;
|
||||
|
||||
ret = bpf_tc_hook_create(hook);
|
||||
if (!ASSERT_OK(ret, "create tc hook"))
|
||||
return ret;
|
||||
|
||||
if (igr_fd >= 0) {
|
||||
hook->attach_point = BPF_TC_INGRESS;
|
||||
ret = bpf_tc_attach(hook, &opts1);
|
||||
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
|
||||
bpf_tc_hook_destroy(hook);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (egr_fd >= 0) {
|
||||
hook->attach_point = BPF_TC_EGRESS;
|
||||
ret = bpf_tc_attach(hook, &opts2);
|
||||
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
|
||||
bpf_tc_hook_destroy(hook);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " NS0);
|
||||
SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " NS1);
|
||||
SYS_NOFAIL("test -f /var/run/netns/" NS2 " && ip netns delete " NS2);
|
||||
}
|
||||
|
||||
static int config_underlay(void)
|
||||
{
|
||||
SYS("ip netns add " NS0);
|
||||
SYS("ip netns add " NS1);
|
||||
SYS("ip netns add " NS2);
|
||||
|
||||
/* NS0 <-> NS1 [veth01 <-> veth10] */
|
||||
SYS("ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1);
|
||||
SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01");
|
||||
SYS("ip -net " NS0 " link set dev veth01 up");
|
||||
SYS("ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10");
|
||||
SYS("ip -net " NS1 " link set dev veth10 up");
|
||||
|
||||
/* NS0 <-> NS2 [veth02 <-> veth20] */
|
||||
SYS("ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2);
|
||||
SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02");
|
||||
SYS("ip -net " NS0 " link set dev veth02 up");
|
||||
SYS("ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20");
|
||||
SYS("ip -net " NS2 " link set dev veth20 up");
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int setup_xfrm_tunnel_ns(const char *ns, const char *ipv4_local,
|
||||
const char *ipv4_remote, int if_id)
|
||||
{
|
||||
/* State: local -> remote */
|
||||
SYS("ip -net %s xfrm state add src %s dst %s spi 1 "
|
||||
ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_local, ipv4_remote, if_id);
|
||||
|
||||
/* State: local <- remote */
|
||||
SYS("ip -net %s xfrm state add src %s dst %s spi 1 "
|
||||
ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_remote, ipv4_local, if_id);
|
||||
|
||||
/* Policy: local -> remote */
|
||||
SYS("ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 "
|
||||
"if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,
|
||||
if_id, ipv4_local, ipv4_remote, if_id);
|
||||
|
||||
/* Policy: local <- remote */
|
||||
SYS("ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 "
|
||||
"if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,
|
||||
if_id, ipv4_remote, ipv4_local, if_id);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int setup_xfrm_tunnel(const char *ns_a, const char *ns_b,
|
||||
const char *ipv4_a, const char *ipv4_b,
|
||||
int if_id_a, int if_id_b)
|
||||
{
|
||||
return setup_xfrm_tunnel_ns(ns_a, ipv4_a, ipv4_b, if_id_a) ||
|
||||
setup_xfrm_tunnel_ns(ns_b, ipv4_b, ipv4_a, if_id_b);
|
||||
}
|
||||
|
||||
static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type,
|
||||
unsigned short len)
|
||||
{
|
||||
struct rtattr *rta =
|
||||
(struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len));
|
||||
rta->rta_type = type;
|
||||
rta->rta_len = RTA_LENGTH(len);
|
||||
nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len);
|
||||
return rta;
|
||||
}
|
||||
|
||||
static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type,
|
||||
const char *s)
|
||||
{
|
||||
struct rtattr *rta = rtattr_add(nh, type, strlen(s));
|
||||
|
||||
memcpy(RTA_DATA(rta), s, strlen(s));
|
||||
return rta;
|
||||
}
|
||||
|
||||
static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type)
|
||||
{
|
||||
return rtattr_add(nh, type, 0);
|
||||
}
|
||||
|
||||
static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr)
|
||||
{
|
||||
uint8_t *end = (uint8_t *)nh + nh->nlmsg_len;
|
||||
|
||||
attr->rta_len = end - (uint8_t *)attr;
|
||||
}
|
||||
|
||||
static int setup_xfrmi_external_dev(const char *ns)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct ifinfomsg info;
|
||||
unsigned char data[128];
|
||||
} req;
|
||||
struct rtattr *link_info, *info_data;
|
||||
struct nstoken *nstoken;
|
||||
int ret = -1, sock = -1;
|
||||
struct nlmsghdr *nh;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
nh = &req.nh;
|
||||
nh->nlmsg_len = NLMSG_LENGTH(sizeof(req.info));
|
||||
nh->nlmsg_type = RTM_NEWLINK;
|
||||
nh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REQUEST;
|
||||
|
||||
rtattr_add_str(nh, IFLA_IFNAME, "ipsec0");
|
||||
link_info = rtattr_begin(nh, IFLA_LINKINFO);
|
||||
rtattr_add_str(nh, IFLA_INFO_KIND, "xfrm");
|
||||
info_data = rtattr_begin(nh, IFLA_INFO_DATA);
|
||||
rtattr_add(nh, IFLA_XFRM_COLLECT_METADATA, 0);
|
||||
rtattr_end(nh, info_data);
|
||||
rtattr_end(nh, link_info);
|
||||
|
||||
nstoken = open_netns(ns);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns"))
|
||||
goto done;
|
||||
|
||||
sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (!ASSERT_GE(sock, 0, "netlink socket"))
|
||||
goto done;
|
||||
ret = send(sock, nh, nh->nlmsg_len, 0);
|
||||
if (!ASSERT_EQ(ret, nh->nlmsg_len, "netlink send length"))
|
||||
goto done;
|
||||
|
||||
ret = 0;
|
||||
done:
|
||||
if (sock != -1)
|
||||
close(sock);
|
||||
if (nstoken)
|
||||
close_netns(nstoken);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int config_overlay(void)
|
||||
{
|
||||
if (setup_xfrm_tunnel(NS0, NS1, IP4_ADDR_VETH01, IP4_ADDR_VETH10,
|
||||
IF_ID_0_TO_1, IF_ID_1))
|
||||
goto fail;
|
||||
if (setup_xfrm_tunnel(NS0, NS2, IP4_ADDR_VETH02, IP4_ADDR_VETH20,
|
||||
IF_ID_0_TO_2, IF_ID_2))
|
||||
goto fail;
|
||||
|
||||
/* Older iproute2 doesn't support this option */
|
||||
if (!ASSERT_OK(setup_xfrmi_external_dev(NS0), "xfrmi"))
|
||||
goto fail;
|
||||
|
||||
SYS("ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0");
|
||||
SYS("ip -net " NS0 " link set dev ipsec0 up");
|
||||
|
||||
SYS("ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1);
|
||||
SYS("ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0");
|
||||
SYS("ip -net " NS1 " link set dev ipsec0 up");
|
||||
|
||||
SYS("ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2);
|
||||
SYS("ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0");
|
||||
SYS("ip -net " NS2 " link set dev ipsec0 up");
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int test_xfrm_ping(struct xfrm_info *skel, u32 if_id)
|
||||
{
|
||||
skel->bss->req_if_id = if_id;
|
||||
|
||||
SYS("ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null");
|
||||
|
||||
if (!ASSERT_EQ(skel->bss->resp_if_id, if_id, "if_id"))
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _test_xfrm_info(void)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
|
||||
int get_xfrm_info_prog_fd, set_xfrm_info_prog_fd;
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct xfrm_info *skel;
|
||||
int ifindex;
|
||||
|
||||
/* load and attach bpf progs to ipsec dev tc hook point */
|
||||
skel = xfrm_info__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "xfrm_info__open_and_load"))
|
||||
goto done;
|
||||
nstoken = open_netns(NS0);
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns " NS0))
|
||||
goto done;
|
||||
ifindex = if_nametoindex("ipsec0");
|
||||
if (!ASSERT_NEQ(ifindex, 0, "ipsec0 ifindex"))
|
||||
goto done;
|
||||
tc_hook.ifindex = ifindex;
|
||||
set_xfrm_info_prog_fd = bpf_program__fd(skel->progs.set_xfrm_info);
|
||||
get_xfrm_info_prog_fd = bpf_program__fd(skel->progs.get_xfrm_info);
|
||||
if (!ASSERT_GE(set_xfrm_info_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (!ASSERT_GE(get_xfrm_info_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, get_xfrm_info_prog_fd,
|
||||
set_xfrm_info_prog_fd))
|
||||
goto done;
|
||||
|
||||
/* perform test */
|
||||
if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_1), 0, "ping " NS1))
|
||||
goto done;
|
||||
if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_2), 0, "ping " NS2))
|
||||
goto done;
|
||||
|
||||
done:
|
||||
if (nstoken)
|
||||
close_netns(nstoken);
|
||||
xfrm_info__destroy(skel);
|
||||
}
|
||||
|
||||
void test_xfrm_info(void)
|
||||
{
|
||||
cleanup();
|
||||
|
||||
if (!ASSERT_OK(config_underlay(), "config_underlay"))
|
||||
goto done;
|
||||
if (!ASSERT_OK(config_overlay(), "config_overlay"))
|
||||
goto done;
|
||||
|
||||
if (test__start_subtest("xfrm_info"))
|
||||
_test_xfrm_info();
|
||||
|
||||
done:
|
||||
cleanup();
|
||||
}
|
|
@ -25,6 +25,9 @@
|
|||
#define IPV6_TCLASS 67
|
||||
#define IPV6_AUTOFLOWLABEL 70
|
||||
|
||||
#define TC_ACT_UNSPEC (-1)
|
||||
#define TC_ACT_SHOT 2
|
||||
|
||||
#define SOL_TCP 6
|
||||
#define TCP_NODELAY 1
|
||||
#define TCP_MAXSEG 2
|
||||
|
|
35
tools/testing/selftests/bpf/progs/xfrm_info.c
Normal file
35
tools/testing/selftests/bpf/progs/xfrm_info.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
// 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";
|
Loading…
Reference in a new issue