Skip to content

Commit b416224

Browse files
mtardyKernel Patches Daemon
authored andcommitted
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. Since it's attached only for this recursion test, it should not disrupt the whole network. Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
1 parent 2135d7f commit b416224

2 files changed

Lines changed: 85 additions & 1 deletion

File tree

tools/testing/selftests/bpf/prog_tests/icmp_send_kfunc.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0
22
#include <test_progs.h>
33
#include <network_helpers.h>
4+
#include <cgroup_helpers.h>
45
#include <linux/errqueue.h>
56
#include <poll.h>
67
#include "icmp_send.skel.h"
@@ -10,6 +11,7 @@
1011
#define ICMP_DEST_UNREACH 3
1112
#define ICMPV6_DEST_UNREACH 1
1213

14+
#define ICMP_HOST_UNREACH 1
1315
#define ICMP_FRAG_NEEDED 4
1416
#define NR_ICMP_UNREACH 15
1517
#define ICMPV6_REJECT_ROUTE 6
@@ -193,11 +195,49 @@ void test_icmp_send_unreach_tc(void)
193195

194196
if (test__start_subtest("ipv4"))
195197
run_icmp_test(skel, AF_INET, "127.0.0.1", NR_ICMP_UNREACH);
196-
197198
if (test__start_subtest("ipv6"))
198199
run_icmp_test(skel, AF_INET6, "::1", ICMPV6_REJECT_ROUTE);
199200

200201
cleanup:
201202
bpf_link__destroy(link);
202203
icmp_send__destroy(skel);
203204
}
205+
206+
void test_icmp_send_unreach_recursion(void)
207+
{
208+
struct icmp_send *skel;
209+
int cgroup_fd = -1;
210+
211+
skel = icmp_send__open_and_load();
212+
if (!ASSERT_OK_PTR(skel, "skel_open"))
213+
goto cleanup;
214+
215+
if (setup_cgroup_environment()) {
216+
fprintf(stderr, "Failed to setup cgroup environment\n");
217+
goto cleanup;
218+
}
219+
220+
cgroup_fd = get_root_cgroup();
221+
if (!ASSERT_GE(cgroup_fd, 0, "get_root_cgroup"))
222+
goto cleanup;
223+
224+
skel->links.recursion =
225+
bpf_program__attach_cgroup(skel->progs.recursion, cgroup_fd);
226+
if (!ASSERT_OK_PTR(skel->links.recursion, "prog_attach_cgroup"))
227+
goto cleanup;
228+
229+
trigger_prog_read_icmp_errqueue(skel, ICMP_HOST_UNREACH, AF_INET,
230+
"127.0.0.1");
231+
232+
/* Because there's recursion involved, the first call will return at
233+
* index 1 since it will return the second, and the second call will
234+
* return at index 0 since it will return the first.
235+
*/
236+
ASSERT_EQ(skel->data->rec_kfunc_rets[0], -EBUSY, "kfunc_rets[0]");
237+
ASSERT_EQ(skel->data->rec_kfunc_rets[1], 0, "kfunc_rets[1]");
238+
239+
cleanup:
240+
cleanup_cgroup_environment();
241+
icmp_send__destroy(skel);
242+
close(cgroup_fd);
243+
}

tools/testing/selftests/bpf/progs/icmp_send.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ int unreach_type = 0;
1414
int unreach_code = 0;
1515
int kfunc_ret = -1;
1616

17+
unsigned int rec_count = 0;
18+
int rec_kfunc_rets[] = { -1, -1 };
19+
1720
SEC("cgroup_skb/egress")
1821
int egress(struct __sk_buff *skb)
1922
{
@@ -125,4 +128,45 @@ int tc_egress(struct __sk_buff *skb)
125128
return TCX_DROP;
126129
}
127130

131+
SEC("cgroup_skb/egress")
132+
int recursion(struct __sk_buff *skb)
133+
{
134+
void *data = (void *)(long)skb->data;
135+
void *data_end = (void *)(long)skb->data_end;
136+
struct tcphdr *tcph;
137+
struct iphdr *iph;
138+
int ret;
139+
140+
iph = data;
141+
if ((void *)(iph + 1) > data_end || iph->version != 4)
142+
return SK_PASS;
143+
144+
if (iph->daddr != bpf_htonl(SERVER_IP))
145+
return SK_PASS;
146+
147+
if (iph->protocol == IPPROTO_TCP) {
148+
tcph = (void *)iph + iph->ihl * 4;
149+
if ((void *)(tcph + 1) > data_end ||
150+
tcph->dest != bpf_htons(server_port))
151+
return SK_PASS;
152+
} else if (iph->protocol != IPPROTO_ICMP) {
153+
return SK_PASS;
154+
}
155+
156+
/* This call will provoke a recursion: the ICMP packet generated by the
157+
* kfunc will re-trigger this program since we are in the root cgroup in
158+
* which the kernel ICMP socket belongs. However when re-entering the
159+
* kfunc, it should return EBUSY.
160+
*/
161+
ret = bpf_icmp_send(skb, unreach_type, unreach_code);
162+
rec_kfunc_rets[rec_count & 1] = ret;
163+
__sync_fetch_and_add(&rec_count, 1);
164+
165+
/* Let the first ICMP error message pass */
166+
if (iph->protocol == IPPROTO_ICMP)
167+
return SK_PASS;
168+
169+
return SK_DROP;
170+
}
171+
128172
char LICENSE[] SEC("license") = "Dual BSD/GPL";

0 commit comments

Comments
 (0)