mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
selftests/bpf: Add bpf_icmp_send recursion test
This test is similar to test_icmp_send_unreach_cgroup but checks that, in case of recursion, meaning that the BPF program calling the kfunc was re-triggered by the icmp_send done by the kfunc, the kfunc will stop early and return -EBUSY. The test attaches to the root cgroup to ensure the ICMP packet generated by the kfunc re-triggers the BPF program. Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> Reviewed-by: Jordan Rife <jordan@jrife.io> Acked-by: Stanislav Fomichev <sdf@fomichev.me> Link: https://lore.kernel.org/bpf/20260709144900.245904-5-mahe.tardy@gmail.com
This commit is contained in:
parent
340a40df94
commit
49d07ba673
|
|
@ -1,8 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
#include <cgroup_helpers.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include "icmp_send.skel.h"
|
||||
|
||||
#define TIMEOUT_MS 1000
|
||||
|
|
@ -10,6 +12,7 @@
|
|||
#define ICMP_DEST_UNREACH 3
|
||||
#define ICMPV6_DEST_UNREACH 1
|
||||
|
||||
#define ICMP_HOST_UNREACH 1
|
||||
#define ICMP_FRAG_NEEDED 4
|
||||
#define NR_ICMP_UNREACH 15
|
||||
#define ICMPV6_REJECT_ROUTE 6
|
||||
|
|
@ -195,3 +198,46 @@ void test_icmp_send_unreach_cgroup(void)
|
|||
if (cgroup_fd >= 0)
|
||||
close(cgroup_fd);
|
||||
}
|
||||
|
||||
void test_icmp_send_unreach_recursion(void)
|
||||
{
|
||||
struct icmp_send *skel;
|
||||
int cgroup_fd = -1;
|
||||
int err;
|
||||
|
||||
err = setup_cgroup_environment();
|
||||
if (!ASSERT_OK(err, "setup_cgroup_environment"))
|
||||
return;
|
||||
|
||||
skel = icmp_send__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_open"))
|
||||
goto cleanup;
|
||||
|
||||
cgroup_fd = get_root_cgroup();
|
||||
if (!ASSERT_OK_FD(cgroup_fd, "get_root_cgroup"))
|
||||
goto cleanup;
|
||||
|
||||
skel->data->target_pid = getpid();
|
||||
skel->links.recursion =
|
||||
bpf_program__attach_cgroup(skel->progs.recursion, cgroup_fd);
|
||||
if (!ASSERT_OK_PTR(skel->links.recursion, "prog_attach_cgroup"))
|
||||
goto cleanup;
|
||||
|
||||
trigger_prog_read_icmp_errqueue(skel, ICMP_HOST_UNREACH, AF_INET,
|
||||
"127.0.0.1");
|
||||
|
||||
/*
|
||||
* Because there's recursion involved, the first call will return at
|
||||
* index 1 since it will return the second, and the second call will
|
||||
* return at index 0 since it will return the first.
|
||||
*/
|
||||
ASSERT_EQ(skel->bss->rec_count, 2, "rec_count");
|
||||
ASSERT_EQ(skel->data->rec_kfunc_rets[0], -EBUSY, "kfunc_rets[0]");
|
||||
ASSERT_EQ(skel->data->rec_kfunc_rets[1], 0, "kfunc_rets[1]");
|
||||
|
||||
cleanup:
|
||||
icmp_send__destroy(skel);
|
||||
if (cgroup_fd >= 0)
|
||||
close(cgroup_fd);
|
||||
cleanup_cgroup_environment();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ __u16 server_port = 0;
|
|||
int unreach_type = 0;
|
||||
int unreach_code = 0;
|
||||
int kfunc_ret = -1;
|
||||
int target_pid = -1;
|
||||
|
||||
unsigned int rec_count = 0;
|
||||
int rec_kfunc_rets[] = { -1, -1 };
|
||||
|
||||
SEC("cgroup_skb/egress")
|
||||
int egress(struct __sk_buff *skb)
|
||||
|
|
@ -65,4 +69,55 @@ int egress(struct __sk_buff *skb)
|
|||
return SK_DROP;
|
||||
}
|
||||
|
||||
SEC("cgroup_skb/egress")
|
||||
int recursion(struct __sk_buff *skb)
|
||||
{
|
||||
void *data = (void *)(long)skb->data;
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
struct icmphdr *icmph;
|
||||
struct tcphdr *tcph;
|
||||
struct iphdr *iph;
|
||||
int ret;
|
||||
|
||||
if ((bpf_get_current_pid_tgid() >> 32) != target_pid)
|
||||
return SK_PASS;
|
||||
|
||||
iph = data;
|
||||
if ((void *)(iph + 1) > data_end || iph->version != 4)
|
||||
return SK_PASS;
|
||||
|
||||
if (iph->daddr != bpf_htonl(SERVER_IP))
|
||||
return SK_PASS;
|
||||
|
||||
if (iph->protocol == IPPROTO_TCP) {
|
||||
tcph = (void *)iph + iph->ihl * 4;
|
||||
if ((void *)(tcph + 1) > data_end ||
|
||||
tcph->dest != bpf_htons(server_port))
|
||||
return SK_PASS;
|
||||
} else if (iph->protocol == IPPROTO_ICMP) {
|
||||
icmph = (void *)iph + iph->ihl * 4;
|
||||
if ((void *)(icmph + 1) > data_end ||
|
||||
icmph->type != unreach_type || icmph->code != unreach_code)
|
||||
return SK_PASS;
|
||||
} else {
|
||||
return SK_PASS;
|
||||
}
|
||||
|
||||
/*
|
||||
* This call will provoke a recursion: the ICMP packet generated by the
|
||||
* kfunc will re-trigger this program since we are in the root cgroup in
|
||||
* which the kernel ICMP socket belongs. However when re-entering the
|
||||
* kfunc, it should return EBUSY.
|
||||
*/
|
||||
ret = bpf_icmp_send(skb, unreach_type, unreach_code);
|
||||
rec_kfunc_rets[rec_count & 1] = ret;
|
||||
__sync_fetch_and_add(&rec_count, 1);
|
||||
|
||||
/* Let the first ICMP error message pass */
|
||||
if (iph->protocol == IPPROTO_ICMP)
|
||||
return SK_PASS;
|
||||
|
||||
return SK_DROP;
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user