Commit 12bb6ca4 authored by Yonghong Song's avatar Yonghong Song Committed by Alexei Starovoitov

selftests/bpf: Add selftests for new cgroup local storage

Add four tests for new cgroup local storage, (1) testing bpf program helpers
and user space map APIs, (2) testing recursive fentry triggering won't deadlock,
(3) testing progs attached to cgroups, and (4) a negative test if the
bpf_cgrp_storage_get() helper key is not a cgroup btf id.
Signed-off-by: default avatarYonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/r/20221026042911.675546-1-yhs@fb.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent fd4ca6c1
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates.*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <test_progs.h>
#include "cgrp_ls_tp_btf.skel.h"
#include "cgrp_ls_recursion.skel.h"
#include "cgrp_ls_attach_cgroup.skel.h"
#include "cgrp_ls_negative.skel.h"
#include "network_helpers.h"
struct socket_cookie {
__u64 cookie_key;
__u32 cookie_value;
};
static void test_tp_btf(int cgroup_fd)
{
struct cgrp_ls_tp_btf *skel;
long val1 = 1, val2 = 0;
int err;
skel = cgrp_ls_tp_btf__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
return;
/* populate a value in map_b */
err = bpf_map_update_elem(bpf_map__fd(skel->maps.map_b), &cgroup_fd, &val1, BPF_ANY);
if (!ASSERT_OK(err, "map_update_elem"))
goto out;
/* check value */
err = bpf_map_lookup_elem(bpf_map__fd(skel->maps.map_b), &cgroup_fd, &val2);
if (!ASSERT_OK(err, "map_lookup_elem"))
goto out;
if (!ASSERT_EQ(val2, 1, "map_lookup_elem, invalid val"))
goto out;
/* delete value */
err = bpf_map_delete_elem(bpf_map__fd(skel->maps.map_b), &cgroup_fd);
if (!ASSERT_OK(err, "map_delete_elem"))
goto out;
skel->bss->target_pid = syscall(SYS_gettid);
err = cgrp_ls_tp_btf__attach(skel);
if (!ASSERT_OK(err, "skel_attach"))
goto out;
syscall(SYS_gettid);
syscall(SYS_gettid);
skel->bss->target_pid = 0;
/* 3x syscalls: 1x attach and 2x gettid */
ASSERT_EQ(skel->bss->enter_cnt, 3, "enter_cnt");
ASSERT_EQ(skel->bss->exit_cnt, 3, "exit_cnt");
ASSERT_EQ(skel->bss->mismatch_cnt, 0, "mismatch_cnt");
out:
cgrp_ls_tp_btf__destroy(skel);
}
static void test_attach_cgroup(int cgroup_fd)
{
int server_fd = 0, client_fd = 0, err = 0;
socklen_t addr_len = sizeof(struct sockaddr_in6);
struct cgrp_ls_attach_cgroup *skel;
__u32 cookie_expected_value;
struct sockaddr_in6 addr;
struct socket_cookie val;
skel = cgrp_ls_attach_cgroup__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open"))
return;
skel->links.set_cookie = bpf_program__attach_cgroup(
skel->progs.set_cookie, cgroup_fd);
if (!ASSERT_OK_PTR(skel->links.set_cookie, "prog_attach"))
goto out;
skel->links.update_cookie_sockops = bpf_program__attach_cgroup(
skel->progs.update_cookie_sockops, cgroup_fd);
if (!ASSERT_OK_PTR(skel->links.update_cookie_sockops, "prog_attach"))
goto out;
skel->links.update_cookie_tracing = bpf_program__attach(
skel->progs.update_cookie_tracing);
if (!ASSERT_OK_PTR(skel->links.update_cookie_tracing, "prog_attach"))
goto out;
server_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0);
if (!ASSERT_GE(server_fd, 0, "start_server"))
goto out;
client_fd = connect_to_fd(server_fd, 0);
if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
goto close_server_fd;
err = bpf_map_lookup_elem(bpf_map__fd(skel->maps.socket_cookies),
&cgroup_fd, &val);
if (!ASSERT_OK(err, "map_lookup(socket_cookies)"))
goto close_client_fd;
err = getsockname(client_fd, (struct sockaddr *)&addr, &addr_len);
if (!ASSERT_OK(err, "getsockname"))
goto close_client_fd;
cookie_expected_value = (ntohs(addr.sin6_port) << 8) | 0xFF;
ASSERT_EQ(val.cookie_value, cookie_expected_value, "cookie_value");
close_client_fd:
close(client_fd);
close_server_fd:
close(server_fd);
out:
cgrp_ls_attach_cgroup__destroy(skel);
}
static void test_recursion(int cgroup_fd)
{
struct cgrp_ls_recursion *skel;
int err;
skel = cgrp_ls_recursion__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
return;
err = cgrp_ls_recursion__attach(skel);
if (!ASSERT_OK(err, "skel_attach"))
goto out;
/* trigger sys_enter, make sure it does not cause deadlock */
syscall(SYS_gettid);
out:
cgrp_ls_recursion__destroy(skel);
}
static void test_negative(void)
{
struct cgrp_ls_negative *skel;
skel = cgrp_ls_negative__open_and_load();
if (!ASSERT_ERR_PTR(skel, "skel_open_and_load")) {
cgrp_ls_negative__destroy(skel);
return;
}
}
void test_cgrp_local_storage(void)
{
int cgroup_fd;
cgroup_fd = test__join_cgroup("/cgrp_local_storage");
if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /cgrp_local_storage"))
return;
if (test__start_subtest("tp_btf"))
test_tp_btf(cgroup_fd);
if (test__start_subtest("attach_cgroup"))
test_attach_cgroup(cgroup_fd);
if (test__start_subtest("recursion"))
test_recursion(cgroup_fd);
if (test__start_subtest("negative"))
test_negative();
close(cgroup_fd);
}
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bpf_tracing_net.h"
char _license[] SEC("license") = "GPL";
struct socket_cookie {
__u64 cookie_key;
__u64 cookie_value;
};
struct {
__uint(type, BPF_MAP_TYPE_CGRP_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, struct socket_cookie);
} socket_cookies SEC(".maps");
SEC("cgroup/connect6")
int set_cookie(struct bpf_sock_addr *ctx)
{
struct socket_cookie *p;
struct tcp_sock *tcp_sk;
struct bpf_sock *sk;
if (ctx->family != AF_INET6 || ctx->user_family != AF_INET6)
return 1;
sk = ctx->sk;
if (!sk)
return 1;
tcp_sk = bpf_skc_to_tcp_sock(sk);
if (!tcp_sk)
return 1;
p = bpf_cgrp_storage_get(&socket_cookies,
tcp_sk->inet_conn.icsk_inet.sk.sk_cgrp_data.cgroup, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!p)
return 1;
p->cookie_value = 0xF;
p->cookie_key = bpf_get_socket_cookie(ctx);
return 1;
}
SEC("sockops")
int update_cookie_sockops(struct bpf_sock_ops *ctx)
{
struct socket_cookie *p;
struct tcp_sock *tcp_sk;
struct bpf_sock *sk;
if (ctx->family != AF_INET6 || ctx->op != BPF_SOCK_OPS_TCP_CONNECT_CB)
return 1;
sk = ctx->sk;
if (!sk)
return 1;
tcp_sk = bpf_skc_to_tcp_sock(sk);
if (!tcp_sk)
return 1;
p = bpf_cgrp_storage_get(&socket_cookies,
tcp_sk->inet_conn.icsk_inet.sk.sk_cgrp_data.cgroup, 0, 0);
if (!p)
return 1;
if (p->cookie_key != bpf_get_socket_cookie(ctx))
return 1;
p->cookie_value |= (ctx->local_port << 8);
return 1;
}
SEC("fexit/inet_stream_connect")
int BPF_PROG(update_cookie_tracing, struct socket *sock,
struct sockaddr *uaddr, int addr_len, int flags)
{
struct socket_cookie *p;
struct tcp_sock *tcp_sk;
if (uaddr->sa_family != AF_INET6)
return 0;
p = bpf_cgrp_storage_get(&socket_cookies, sock->sk->sk_cgrp_data.cgroup, 0, 0);
if (!p)
return 0;
if (p->cookie_key != bpf_get_socket_cookie(sock->sk))
return 0;
p->cookie_value |= 0xF0;
return 0;
}
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char _license[] SEC("license") = "GPL";
struct {
__uint(type, BPF_MAP_TYPE_CGRP_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, long);
} map_a SEC(".maps");
SEC("tp_btf/sys_enter")
int BPF_PROG(on_enter, struct pt_regs *regs, long id)
{
struct task_struct *task;
task = bpf_get_current_task_btf();
(void)bpf_cgrp_storage_get(&map_a, (struct cgroup *)task, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
return 0;
}
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char _license[] SEC("license") = "GPL";
struct {
__uint(type, BPF_MAP_TYPE_CGRP_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, long);
} map_a SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_CGRP_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, long);
} map_b SEC(".maps");
SEC("fentry/bpf_local_storage_lookup")
int BPF_PROG(on_lookup)
{
struct task_struct *task = bpf_get_current_task_btf();
bpf_cgrp_storage_delete(&map_a, task->cgroups->dfl_cgrp);
bpf_cgrp_storage_delete(&map_b, task->cgroups->dfl_cgrp);
return 0;
}
SEC("fentry/bpf_local_storage_update")
int BPF_PROG(on_update)
{
struct task_struct *task = bpf_get_current_task_btf();
long *ptr;
ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (ptr)
*ptr += 1;
ptr = bpf_cgrp_storage_get(&map_b, task->cgroups->dfl_cgrp, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (ptr)
*ptr += 1;
return 0;
}
SEC("tp_btf/sys_enter")
int BPF_PROG(on_enter, struct pt_regs *regs, long id)
{
struct task_struct *task;
long *ptr;
task = bpf_get_current_task_btf();
ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (ptr)
*ptr = 200;
ptr = bpf_cgrp_storage_get(&map_b, task->cgroups->dfl_cgrp, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (ptr)
*ptr = 100;
return 0;
}
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char _license[] SEC("license") = "GPL";
struct {
__uint(type, BPF_MAP_TYPE_CGRP_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, long);
} map_a SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_CGRP_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, long);
} map_b SEC(".maps");
#define MAGIC_VALUE 0xabcd1234
pid_t target_pid = 0;
int mismatch_cnt = 0;
int enter_cnt = 0;
int exit_cnt = 0;
SEC("tp_btf/sys_enter")
int BPF_PROG(on_enter, struct pt_regs *regs, long id)
{
struct task_struct *task;
long *ptr;
int err;
task = bpf_get_current_task_btf();
if (task->pid != target_pid)
return 0;
/* populate value 0 */
ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!ptr)
return 0;
/* delete value 0 */
err = bpf_cgrp_storage_delete(&map_a, task->cgroups->dfl_cgrp);
if (err)
return 0;
/* value is not available */
ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0, 0);
if (ptr)
return 0;
/* re-populate the value */
ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!ptr)
return 0;
__sync_fetch_and_add(&enter_cnt, 1);
*ptr = MAGIC_VALUE + enter_cnt;
return 0;
}
SEC("tp_btf/sys_exit")
int BPF_PROG(on_exit, struct pt_regs *regs, long id)
{
struct task_struct *task;
long *ptr;
task = bpf_get_current_task_btf();
if (task->pid != target_pid)
return 0;
ptr = bpf_cgrp_storage_get(&map_a, task->cgroups->dfl_cgrp, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!ptr)
return 0;
__sync_fetch_and_add(&exit_cnt, 1);
if (*ptr != MAGIC_VALUE + exit_cnt)
__sync_fetch_and_add(&mismatch_cnt, 1);
return 0;
}
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