selftests/bpf: Add sockmap test for redirecting partial skb data

Add a test case to check whether sockmap redirection works correctly
when data length returned by stream_parser is less than skb->len.

In addition, this test checks whether strp_done is called correctly.
The reason is that we returns skb->len - 1 from the stream_parser, so
the last byte in the skb will be held by strp->skb_head. Therefore,
if strp_done is not called to free strp->skb_head, we'll get a memleak
warning.

Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
Link: https://lore.kernel.org/r/20230804073740.194770-5-xukuohai@huaweicloud.com
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
Xu Kuohai 2023-08-04 03:37:40 -04:00 committed by Martin KaFai Lau
parent 90f0074cd9
commit a4b7193d8e
2 changed files with 86 additions and 0 deletions

View File

@ -869,6 +869,77 @@ static void test_msg_redir_to_listening(struct test_sockmap_listen *skel,
xbpf_prog_detach2(verdict, sock_map, BPF_SK_MSG_VERDICT); xbpf_prog_detach2(verdict, sock_map, BPF_SK_MSG_VERDICT);
} }
static void redir_partial(int family, int sotype, int sock_map, int parser_map)
{
int s, c0, c1, p0, p1;
int err, n, key, value;
char buf[] = "abc";
key = 0;
value = sizeof(buf) - 1;
err = xbpf_map_update_elem(parser_map, &key, &value, 0);
if (err)
return;
s = socket_loopback(family, sotype | SOCK_NONBLOCK);
if (s < 0)
goto clean_parser_map;
err = create_socket_pairs(s, family, sotype, &c0, &c1, &p0, &p1);
if (err)
goto close_srv;
err = add_to_sockmap(sock_map, p0, p1);
if (err)
goto close;
n = xsend(c1, buf, sizeof(buf), 0);
if (n < sizeof(buf))
FAIL("incomplete write");
n = xrecv_nonblock(c0, buf, sizeof(buf), 0);
if (n != sizeof(buf) - 1)
FAIL("expect %zu, received %d", sizeof(buf) - 1, n);
close:
xclose(c0);
xclose(p0);
xclose(c1);
xclose(p1);
close_srv:
xclose(s);
clean_parser_map:
key = 0;
value = 0;
xbpf_map_update_elem(parser_map, &key, &value, 0);
}
static void test_skb_redir_partial(struct test_sockmap_listen *skel,
struct bpf_map *inner_map, int family,
int sotype)
{
int verdict = bpf_program__fd(skel->progs.prog_stream_verdict);
int parser = bpf_program__fd(skel->progs.prog_stream_parser);
int parser_map = bpf_map__fd(skel->maps.parser_map);
int sock_map = bpf_map__fd(inner_map);
int err;
err = xbpf_prog_attach(parser, sock_map, BPF_SK_SKB_STREAM_PARSER, 0);
if (err)
return;
err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_STREAM_VERDICT, 0);
if (err)
goto detach;
redir_partial(family, sotype, sock_map, parser_map);
xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_STREAM_VERDICT);
detach:
xbpf_prog_detach2(parser, sock_map, BPF_SK_SKB_STREAM_PARSER);
}
static void test_reuseport_select_listening(int family, int sotype, static void test_reuseport_select_listening(int family, int sotype,
int sock_map, int verd_map, int sock_map, int verd_map,
int reuseport_prog) int reuseport_prog)
@ -1243,6 +1314,7 @@ static void test_redir(struct test_sockmap_listen *skel, struct bpf_map *map,
} tests[] = { } tests[] = {
TEST(test_skb_redir_to_connected), TEST(test_skb_redir_to_connected),
TEST(test_skb_redir_to_listening), TEST(test_skb_redir_to_listening),
TEST(test_skb_redir_partial),
TEST(test_msg_redir_to_connected), TEST(test_msg_redir_to_connected),
TEST(test_msg_redir_to_listening), TEST(test_msg_redir_to_listening),
}; };

View File

@ -28,12 +28,26 @@ struct {
__type(value, unsigned int); __type(value, unsigned int);
} verdict_map SEC(".maps"); } verdict_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
} parser_map SEC(".maps");
bool test_sockmap = false; /* toggled by user-space */ bool test_sockmap = false; /* toggled by user-space */
bool test_ingress = false; /* toggled by user-space */ bool test_ingress = false; /* toggled by user-space */
SEC("sk_skb/stream_parser") SEC("sk_skb/stream_parser")
int prog_stream_parser(struct __sk_buff *skb) int prog_stream_parser(struct __sk_buff *skb)
{ {
int *value;
__u32 key = 0;
value = bpf_map_lookup_elem(&parser_map, &key);
if (value && *value)
return *value;
return skb->len; return skb->len;
} }