Commit 437ffcb0 authored by Andrii Nakryiko's avatar Andrii Nakryiko

Merge branch 'current_pid_tgid-for-all-prog-types'

Yonghong Song says:

====================
current_pid_tgid() for all prog types

Currently bpf_get_current_pid_tgid() is allowed in tracing, cgroup
and sk_msg progs while bpf_get_ns_current_pid_tgid() is only allowed
in tracing progs.

We have an internal use case where for an application running
in a container (with pid namespace), user wants to get
the pid associated with the pid namespace in a cgroup bpf
program. Besides cgroup, the only prog type, supporting
bpf_get_current_pid_tgid() but not bpf_get_ns_current_pid_tgid(),
is sk_msg.

But actually both bpf_get_current_pid_tgid() and
bpf_get_ns_current_pid_tgid() helpers do not reveal kernel internal
data and there is no reason that they cannot be used in other
program types. This patch just did this and enabled these
two helpers for all program types.

Patch 1 added the kernel support and patches 2-5 added
the test for cgroup and sk_msg.

Change logs:
  v1 -> v2:
    - allow bpf_get_[ns_]current_pid_tgid() for all prog types.
    - for network related selftests, using netns.
====================

Link: https://lore.kernel.org/r/20240315184849.2974556-1-yonghong.song@linux.devSigned-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents 1a4a0cb7 4c195ee4
......@@ -2575,8 +2575,6 @@ cgroup_current_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
switch (func_id) {
case BPF_FUNC_get_current_uid_gid:
return &bpf_get_current_uid_gid_proto;
case BPF_FUNC_get_current_pid_tgid:
return &bpf_get_current_pid_tgid_proto;
case BPF_FUNC_get_current_comm:
return &bpf_get_current_comm_proto;
#ifdef CONFIG_CGROUP_NET_CLASSID
......
......@@ -1730,6 +1730,10 @@ bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_strtol_proto;
case BPF_FUNC_strtoul:
return &bpf_strtoul_proto;
case BPF_FUNC_get_current_pid_tgid:
return &bpf_get_current_pid_tgid_proto;
case BPF_FUNC_get_ns_current_pid_tgid:
return &bpf_get_ns_current_pid_tgid_proto;
default:
break;
}
......
......@@ -1525,8 +1525,6 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_ktime_get_boot_ns_proto;
case BPF_FUNC_tail_call:
return &bpf_tail_call_proto;
case BPF_FUNC_get_current_pid_tgid:
return &bpf_get_current_pid_tgid_proto;
case BPF_FUNC_get_current_task:
return &bpf_get_current_task_proto;
case BPF_FUNC_get_current_task_btf:
......@@ -1582,8 +1580,6 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_send_signal_thread_proto;
case BPF_FUNC_perf_event_read_value:
return &bpf_perf_event_read_value_proto;
case BPF_FUNC_get_ns_current_pid_tgid:
return &bpf_get_ns_current_pid_tgid_proto;
case BPF_FUNC_ringbuf_output:
return &bpf_ringbuf_output_proto;
case BPF_FUNC_ringbuf_reserve:
......
......@@ -8342,8 +8342,6 @@ sk_msg_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_event_output_data_proto;
case BPF_FUNC_get_current_uid_gid:
return &bpf_get_current_uid_gid_proto;
case BPF_FUNC_get_current_pid_tgid:
return &bpf_get_current_pid_tgid_proto;
case BPF_FUNC_sk_storage_get:
return &bpf_sk_storage_get_proto;
case BPF_FUNC_sk_storage_delete:
......
......@@ -12,77 +12,229 @@
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/fcntl.h>
#include "network_helpers.h"
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
static int test_current_pid_tgid(void *args)
static int get_pid_tgid(pid_t *pid, pid_t *tgid,
struct test_ns_current_pid_tgid__bss *bss)
{
struct test_ns_current_pid_tgid__bss *bss;
struct test_ns_current_pid_tgid *skel;
int err = -1, duration = 0;
pid_t tgid, pid;
struct stat st;
int err;
skel = test_ns_current_pid_tgid__open_and_load();
if (CHECK(!skel, "skel_open_load", "failed to load skeleton\n"))
goto cleanup;
pid = syscall(SYS_gettid);
tgid = getpid();
*pid = syscall(SYS_gettid);
*tgid = getpid();
err = stat("/proc/self/ns/pid", &st);
if (CHECK(err, "stat", "failed /proc/self/ns/pid: %d\n", err))
goto cleanup;
if (!ASSERT_OK(err, "stat /proc/self/ns/pid"))
return err;
bss = skel->bss;
bss->dev = st.st_dev;
bss->ino = st.st_ino;
bss->user_pid = 0;
bss->user_tgid = 0;
return 0;
}
static int test_current_pid_tgid_tp(void *args)
{
struct test_ns_current_pid_tgid__bss *bss;
struct test_ns_current_pid_tgid *skel;
int ret = -1, err;
pid_t tgid, pid;
skel = test_ns_current_pid_tgid__open();
if (!ASSERT_OK_PTR(skel, "test_ns_current_pid_tgid__open"))
return ret;
bpf_program__set_autoload(skel->progs.tp_handler, true);
err = test_ns_current_pid_tgid__load(skel);
if (!ASSERT_OK(err, "test_ns_current_pid_tgid__load"))
goto cleanup;
bss = skel->bss;
if (get_pid_tgid(&pid, &tgid, bss))
goto cleanup;
err = test_ns_current_pid_tgid__attach(skel);
if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
if (!ASSERT_OK(err, "test_ns_current_pid_tgid__attach"))
goto cleanup;
/* trigger tracepoint */
usleep(1);
ASSERT_EQ(bss->user_pid, pid, "pid");
ASSERT_EQ(bss->user_tgid, tgid, "tgid");
err = 0;
if (!ASSERT_EQ(bss->user_pid, pid, "pid"))
goto cleanup;
if (!ASSERT_EQ(bss->user_tgid, tgid, "tgid"))
goto cleanup;
ret = 0;
cleanup:
test_ns_current_pid_tgid__destroy(skel);
return ret;
}
static int test_current_pid_tgid_cgrp(void *args)
{
struct test_ns_current_pid_tgid__bss *bss;
struct test_ns_current_pid_tgid *skel;
int server_fd = -1, ret = -1, err;
int cgroup_fd = *(int *)args;
pid_t tgid, pid;
skel = test_ns_current_pid_tgid__open();
if (!ASSERT_OK_PTR(skel, "test_ns_current_pid_tgid__open"))
return ret;
bpf_program__set_autoload(skel->progs.cgroup_bind4, true);
err = test_ns_current_pid_tgid__load(skel);
if (!ASSERT_OK(err, "test_ns_current_pid_tgid__load"))
goto cleanup;
bss = skel->bss;
if (get_pid_tgid(&pid, &tgid, bss))
goto cleanup;
skel->links.cgroup_bind4 = bpf_program__attach_cgroup(
skel->progs.cgroup_bind4, cgroup_fd);
if (!ASSERT_OK_PTR(skel->links.cgroup_bind4, "bpf_program__attach_cgroup"))
goto cleanup;
server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
if (!ASSERT_GE(server_fd, 0, "start_server"))
goto cleanup;
if (!ASSERT_EQ(bss->user_pid, pid, "pid"))
goto cleanup;
if (!ASSERT_EQ(bss->user_tgid, tgid, "tgid"))
goto cleanup;
ret = 0;
cleanup:
test_ns_current_pid_tgid__destroy(skel);
if (server_fd >= 0)
close(server_fd);
test_ns_current_pid_tgid__destroy(skel);
return ret;
}
static int test_current_pid_tgid_sk_msg(void *args)
{
int verdict, map, server_fd = -1, client_fd = -1;
struct test_ns_current_pid_tgid__bss *bss;
static const char send_msg[] = "message";
struct test_ns_current_pid_tgid *skel;
int ret = -1, err, key = 0;
pid_t tgid, pid;
skel = test_ns_current_pid_tgid__open();
if (!ASSERT_OK_PTR(skel, "test_ns_current_pid_tgid__open"))
return ret;
bpf_program__set_autoload(skel->progs.sk_msg, true);
err = test_ns_current_pid_tgid__load(skel);
if (!ASSERT_OK(err, "test_ns_current_pid_tgid__load"))
goto cleanup;
bss = skel->bss;
if (get_pid_tgid(&pid, &tgid, skel->bss))
goto cleanup;
verdict = bpf_program__fd(skel->progs.sk_msg);
map = bpf_map__fd(skel->maps.sock_map);
err = bpf_prog_attach(verdict, map, BPF_SK_MSG_VERDICT, 0);
if (!ASSERT_OK(err, "prog_attach"))
goto cleanup;
server_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0);
if (!ASSERT_GE(server_fd, 0, "start_server"))
goto cleanup;
return err;
client_fd = connect_to_fd(server_fd, 0);
if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
goto cleanup;
err = bpf_map_update_elem(map, &key, &client_fd, BPF_ANY);
if (!ASSERT_OK(err, "bpf_map_update_elem"))
goto cleanup;
err = send(client_fd, send_msg, sizeof(send_msg), 0);
if (!ASSERT_EQ(err, sizeof(send_msg), "send(msg)"))
goto cleanup;
if (!ASSERT_EQ(bss->user_pid, pid, "pid"))
goto cleanup;
if (!ASSERT_EQ(bss->user_tgid, tgid, "tgid"))
goto cleanup;
ret = 0;
cleanup:
if (server_fd >= 0)
close(server_fd);
if (client_fd >= 0)
close(client_fd);
test_ns_current_pid_tgid__destroy(skel);
return ret;
}
static void test_ns_current_pid_tgid_new_ns(void)
static void test_ns_current_pid_tgid_new_ns(int (*fn)(void *), void *arg)
{
int wstatus, duration = 0;
int wstatus;
pid_t cpid;
/* Create a process in a new namespace, this process
* will be the init process of this new namespace hence will be pid 1.
*/
cpid = clone(test_current_pid_tgid, child_stack + STACK_SIZE,
CLONE_NEWPID | SIGCHLD, NULL);
cpid = clone(fn, child_stack + STACK_SIZE,
CLONE_NEWPID | SIGCHLD, arg);
if (CHECK(cpid == -1, "clone", "%s\n", strerror(errno)))
if (!ASSERT_NEQ(cpid, -1, "clone"))
return;
if (CHECK(waitpid(cpid, &wstatus, 0) == -1, "waitpid", "%s\n", strerror(errno)))
if (!ASSERT_NEQ(waitpid(cpid, &wstatus, 0), -1, "waitpid"))
return;
if (CHECK(WEXITSTATUS(wstatus) != 0, "newns_pidtgid", "failed"))
if (!ASSERT_OK(WEXITSTATUS(wstatus), "newns_pidtgid"))
return;
}
static void test_in_netns(int (*fn)(void *), void *arg)
{
struct nstoken *nstoken = NULL;
SYS(cleanup, "ip netns add ns_current_pid_tgid");
SYS(cleanup, "ip -net ns_current_pid_tgid link set dev lo up");
nstoken = open_netns("ns_current_pid_tgid");
if (!ASSERT_OK_PTR(nstoken, "open_netns"))
goto cleanup;
test_ns_current_pid_tgid_new_ns(fn, arg);
cleanup:
if (nstoken)
close_netns(nstoken);
SYS_NOFAIL("ip netns del ns_current_pid_tgid");
}
/* TODO: use a different tracepoint */
void serial_test_ns_current_pid_tgid(void)
{
if (test__start_subtest("ns_current_pid_tgid_root_ns"))
test_current_pid_tgid(NULL);
if (test__start_subtest("ns_current_pid_tgid_new_ns"))
test_ns_current_pid_tgid_new_ns();
if (test__start_subtest("root_ns_tp"))
test_current_pid_tgid_tp(NULL);
if (test__start_subtest("new_ns_tp"))
test_ns_current_pid_tgid_new_ns(test_current_pid_tgid_tp, NULL);
if (test__start_subtest("new_ns_cgrp")) {
int cgroup_fd = -1;
cgroup_fd = test__join_cgroup("/sock_addr");
if (ASSERT_GE(cgroup_fd, 0, "join_cgroup")) {
test_in_netns(test_current_pid_tgid_cgrp, &cgroup_fd);
close(cgroup_fd);
}
}
if (test__start_subtest("new_ns_sk_msg"))
test_in_netns(test_current_pid_tgid_sk_msg, NULL);
}
......@@ -5,23 +5,48 @@
#include <stdint.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_SOCKMAP);
__uint(max_entries, 2);
__type(key, __u32);
__type(value, __u32);
} sock_map SEC(".maps");
__u64 user_pid = 0;
__u64 user_tgid = 0;
__u64 dev = 0;
__u64 ino = 0;
SEC("tracepoint/syscalls/sys_enter_nanosleep")
int handler(const void *ctx)
static void get_pid_tgid(void)
{
struct bpf_pidns_info nsdata;
if (bpf_get_ns_current_pid_tgid(dev, ino, &nsdata, sizeof(struct bpf_pidns_info)))
return 0;
return;
user_pid = nsdata.pid;
user_tgid = nsdata.tgid;
}
SEC("?tracepoint/syscalls/sys_enter_nanosleep")
int tp_handler(const void *ctx)
{
get_pid_tgid();
return 0;
}
SEC("?cgroup/bind4")
int cgroup_bind4(struct bpf_sock_addr *ctx)
{
get_pid_tgid();
return 1;
}
SEC("?sk_msg")
int sk_msg(struct sk_msg_md *msg)
{
get_pid_tgid();
return SK_PASS;
}
char _license[] SEC("license") = "GPL";
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