mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 16:54:20 +08:00
Merge branch 'BPF iterator for UNIX domain socket.'
Kuniyuki Iwashima says: ==================== This patch set adds BPF iterator support for UNIX domain socket. The first patch implements it, and the second adds "%c" support for BPF_SEQ_PRINTF(). Thanks to Yonghong Song for the fix [0] for the LLVM code gen. The fix prevents the LLVM compiler from transforming the loop exit condition '<' to '!=', where the upper bound is not a constant. The transformation leads the verifier to interpret it as an infinite loop. And thanks to Andrii Nakryiko for its workaround [1]. [0] https://reviews.llvm.org/D107483 [1] https://lore.kernel.org/netdev/CAEf4BzZ3sVx1m1mOCcPcuVPiY6cWEAO=6VGHDiXEs9ZVD-RoLg@mail.gmail.com/ Changelog: v6: - Align the header "Inde" column - Change int vars to __u64 not to break test_progs-no_alu32 - Move the if statement into the for loop not to depend on the fix [0] - Drop the README change - Modify "%c" positive test patterns v5: https://lore.kernel.org/netdev/20210812164557.79046-1-kuniyu@amazon.co.jp/ - Align header line of bpf_iter_unix.c - Add test for "%c" v4: https://lore.kernel.org/netdev/20210810092807.13190-1-kuniyu@amazon.co.jp/ - Check IS_BUILTIN(CONFIG_UNIX) - Support "%c" in BPF_SEQ_PRINTF() - Uncomment the code to print the name of the abstract socket - Mention the LLVM fix in README.rst - Remove the 'aligned' attribute in bpf_iter.h - Keep the format string on a single line v3: https://lore.kernel.org/netdev/20210804070851.97834-1-kuniyu@amazon.co.jp/ - Export some functions for CONFIG_UNIX=m v2: https://lore.kernel.org/netdev/20210803011110.21205-1-kuniyu@amazon.co.jp/ - Implement bpf_iter specific seq_ops->stop() - Add bpf_iter__unix in bpf_iter.h - Move common definitions in selftest to bpf_tracing_net.h - Include the code for abstract UNIX domain socket as comment in selftest - Use ASSERT_OK_PTR() instead of CHECK() - Make ternary operators on single line v1: https://lore.kernel.org/netdev/20210729233645.4869-1-kuniyu@amazon.co.jp/ ==================== Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
This commit is contained in:
commit
fa183a86ee
@ -172,7 +172,8 @@ extern struct btf_id_set name;
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP_TW, tcp_timewait_sock) \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP6, tcp6_sock) \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP, udp_sock) \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP6, udp6_sock)
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP6, udp6_sock) \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_UNIX, unix_sock)
|
||||
|
||||
enum {
|
||||
#define BTF_SOCK_TYPE(name, str) name,
|
||||
|
@ -907,6 +907,20 @@ fmt_str:
|
||||
tmp_buf += err;
|
||||
num_spec++;
|
||||
|
||||
continue;
|
||||
} else if (fmt[i] == 'c') {
|
||||
if (!tmp_buf)
|
||||
goto nocopy_fmt;
|
||||
|
||||
if (tmp_buf_end == tmp_buf) {
|
||||
err = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*tmp_buf = raw_args[num_spec];
|
||||
tmp_buf++;
|
||||
num_spec++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,7 @@
|
||||
#include <linux/security.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#include "scm.h"
|
||||
|
||||
@ -3143,6 +3144,64 @@ static const struct seq_operations unix_seq_ops = {
|
||||
.stop = unix_seq_stop,
|
||||
.show = unix_seq_show,
|
||||
};
|
||||
|
||||
#if IS_BUILTIN(CONFIG_UNIX) && defined(CONFIG_BPF_SYSCALL)
|
||||
struct bpf_iter__unix {
|
||||
__bpf_md_ptr(struct bpf_iter_meta *, meta);
|
||||
__bpf_md_ptr(struct unix_sock *, unix_sk);
|
||||
uid_t uid __aligned(8);
|
||||
};
|
||||
|
||||
static int unix_prog_seq_show(struct bpf_prog *prog, struct bpf_iter_meta *meta,
|
||||
struct unix_sock *unix_sk, uid_t uid)
|
||||
{
|
||||
struct bpf_iter__unix ctx;
|
||||
|
||||
meta->seq_num--; /* skip SEQ_START_TOKEN */
|
||||
ctx.meta = meta;
|
||||
ctx.unix_sk = unix_sk;
|
||||
ctx.uid = uid;
|
||||
return bpf_iter_run_prog(prog, &ctx);
|
||||
}
|
||||
|
||||
static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct bpf_iter_meta meta;
|
||||
struct bpf_prog *prog;
|
||||
struct sock *sk = v;
|
||||
uid_t uid;
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
return 0;
|
||||
|
||||
uid = from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk));
|
||||
meta.seq = seq;
|
||||
prog = bpf_iter_get_info(&meta, false);
|
||||
return unix_prog_seq_show(prog, &meta, v, uid);
|
||||
}
|
||||
|
||||
static void bpf_iter_unix_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct bpf_iter_meta meta;
|
||||
struct bpf_prog *prog;
|
||||
|
||||
if (!v) {
|
||||
meta.seq = seq;
|
||||
prog = bpf_iter_get_info(&meta, true);
|
||||
if (prog)
|
||||
(void)unix_prog_seq_show(prog, &meta, v, 0);
|
||||
}
|
||||
|
||||
unix_seq_stop(seq, v);
|
||||
}
|
||||
|
||||
static const struct seq_operations bpf_iter_unix_seq_ops = {
|
||||
.start = unix_seq_start,
|
||||
.next = unix_seq_next,
|
||||
.stop = bpf_iter_unix_seq_stop,
|
||||
.show = bpf_iter_unix_seq_show,
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const struct net_proto_family unix_family_ops = {
|
||||
@ -3183,6 +3242,35 @@ static struct pernet_operations unix_net_ops = {
|
||||
.exit = unix_net_exit,
|
||||
};
|
||||
|
||||
#if IS_BUILTIN(CONFIG_UNIX) && defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
|
||||
DEFINE_BPF_ITER_FUNC(unix, struct bpf_iter_meta *meta,
|
||||
struct unix_sock *unix_sk, uid_t uid)
|
||||
|
||||
static const struct bpf_iter_seq_info unix_seq_info = {
|
||||
.seq_ops = &bpf_iter_unix_seq_ops,
|
||||
.init_seq_private = bpf_iter_init_seq_net,
|
||||
.fini_seq_private = bpf_iter_fini_seq_net,
|
||||
.seq_priv_size = sizeof(struct seq_net_private),
|
||||
};
|
||||
|
||||
static struct bpf_iter_reg unix_reg_info = {
|
||||
.target = "unix",
|
||||
.ctx_arg_info_size = 1,
|
||||
.ctx_arg_info = {
|
||||
{ offsetof(struct bpf_iter__unix, unix_sk),
|
||||
PTR_TO_BTF_ID_OR_NULL },
|
||||
},
|
||||
.seq_info = &unix_seq_info,
|
||||
};
|
||||
|
||||
static void __init bpf_iter_register(void)
|
||||
{
|
||||
unix_reg_info.ctx_arg_info[0].btf_id = btf_sock_ids[BTF_SOCK_TYPE_UNIX];
|
||||
if (bpf_iter_reg_target(&unix_reg_info))
|
||||
pr_warn("Warning: could not register bpf iterator unix\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init af_unix_init(void)
|
||||
{
|
||||
int rc = -1;
|
||||
@ -3198,6 +3286,11 @@ static int __init af_unix_init(void)
|
||||
sock_register(&unix_family_ops);
|
||||
register_pernet_subsys(&unix_net_ops);
|
||||
unix_bpf_build_proto();
|
||||
|
||||
#if IS_BUILTIN(CONFIG_UNIX) && defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
|
||||
bpf_iter_register();
|
||||
#endif
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "bpf_iter_tcp6.skel.h"
|
||||
#include "bpf_iter_udp4.skel.h"
|
||||
#include "bpf_iter_udp6.skel.h"
|
||||
#include "bpf_iter_unix.skel.h"
|
||||
#include "bpf_iter_test_kern1.skel.h"
|
||||
#include "bpf_iter_test_kern2.skel.h"
|
||||
#include "bpf_iter_test_kern3.skel.h"
|
||||
@ -313,6 +314,19 @@ static void test_udp6(void)
|
||||
bpf_iter_udp6__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_unix(void)
|
||||
{
|
||||
struct bpf_iter_unix *skel;
|
||||
|
||||
skel = bpf_iter_unix__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "bpf_iter_unix__open_and_load"))
|
||||
return;
|
||||
|
||||
do_dummy_read(skel->progs.dump_unix);
|
||||
|
||||
bpf_iter_unix__destroy(skel);
|
||||
}
|
||||
|
||||
/* The expected string is less than 16 bytes */
|
||||
static int do_read_with_fd(int iter_fd, const char *expected,
|
||||
bool read_one_char)
|
||||
@ -1255,6 +1269,8 @@ void test_bpf_iter(void)
|
||||
test_udp4();
|
||||
if (test__start_subtest("udp6"))
|
||||
test_udp6();
|
||||
if (test__start_subtest("unix"))
|
||||
test_unix();
|
||||
if (test__start_subtest("anon"))
|
||||
test_anon_iter(false);
|
||||
if (test__start_subtest("anon-read-one-char"))
|
||||
|
@ -19,7 +19,7 @@
|
||||
#define EXP_ADDR_OUT "0000000000000000 ffff00000add4e55 "
|
||||
#define EXP_ADDR_RET sizeof(EXP_ADDR_OUT "unknownhashedptr")
|
||||
|
||||
#define EXP_STR_OUT "str1 longstr"
|
||||
#define EXP_STR_OUT "str1 a b c d e longstr"
|
||||
#define EXP_STR_RET sizeof(EXP_STR_OUT)
|
||||
|
||||
#define EXP_OVER_OUT "%over"
|
||||
@ -114,6 +114,8 @@ void test_snprintf_negative(void)
|
||||
ASSERT_ERR(load_single_snprintf("%"), "invalid specifier 3");
|
||||
ASSERT_ERR(load_single_snprintf("%12345678"), "invalid specifier 4");
|
||||
ASSERT_ERR(load_single_snprintf("%--------"), "invalid specifier 5");
|
||||
ASSERT_ERR(load_single_snprintf("%lc"), "invalid specifier 6");
|
||||
ASSERT_ERR(load_single_snprintf("%llc"), "invalid specifier 7");
|
||||
ASSERT_ERR(load_single_snprintf("\x80"), "non ascii character");
|
||||
ASSERT_ERR(load_single_snprintf("\x1"), "non printable character");
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#define tcp6_sock tcp6_sock___not_used
|
||||
#define bpf_iter__udp bpf_iter__udp___not_used
|
||||
#define udp6_sock udp6_sock___not_used
|
||||
#define bpf_iter__unix bpf_iter__unix___not_used
|
||||
#define bpf_iter__bpf_map_elem bpf_iter__bpf_map_elem___not_used
|
||||
#define bpf_iter__bpf_sk_storage_map bpf_iter__bpf_sk_storage_map___not_used
|
||||
#define bpf_iter__sockmap bpf_iter__sockmap___not_used
|
||||
@ -32,6 +33,7 @@
|
||||
#undef tcp6_sock
|
||||
#undef bpf_iter__udp
|
||||
#undef udp6_sock
|
||||
#undef bpf_iter__unix
|
||||
#undef bpf_iter__bpf_map_elem
|
||||
#undef bpf_iter__bpf_sk_storage_map
|
||||
#undef bpf_iter__sockmap
|
||||
@ -103,6 +105,12 @@ struct udp6_sock {
|
||||
struct ipv6_pinfo inet6;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct bpf_iter__unix {
|
||||
struct bpf_iter_meta *meta;
|
||||
struct unix_sock *unix_sk;
|
||||
uid_t uid;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct bpf_iter__bpf_map_elem {
|
||||
struct bpf_iter_meta *meta;
|
||||
struct bpf_map *map;
|
||||
|
80
tools/testing/selftests/bpf/progs/bpf_iter_unix.c
Normal file
80
tools/testing/selftests/bpf/progs/bpf_iter_unix.c
Normal file
@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright Amazon.com Inc. or its affiliates. */
|
||||
#include "bpf_iter.h"
|
||||
#include "bpf_tracing_net.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static long sock_i_ino(const struct sock *sk)
|
||||
{
|
||||
const struct socket *sk_socket = sk->sk_socket;
|
||||
const struct inode *inode;
|
||||
unsigned long ino;
|
||||
|
||||
if (!sk_socket)
|
||||
return 0;
|
||||
|
||||
inode = &container_of(sk_socket, struct socket_alloc, socket)->vfs_inode;
|
||||
bpf_probe_read_kernel(&ino, sizeof(ino), &inode->i_ino);
|
||||
return ino;
|
||||
}
|
||||
|
||||
SEC("iter/unix")
|
||||
int dump_unix(struct bpf_iter__unix *ctx)
|
||||
{
|
||||
struct unix_sock *unix_sk = ctx->unix_sk;
|
||||
struct sock *sk = (struct sock *)unix_sk;
|
||||
struct seq_file *seq;
|
||||
__u32 seq_num;
|
||||
|
||||
if (!unix_sk)
|
||||
return 0;
|
||||
|
||||
seq = ctx->meta->seq;
|
||||
seq_num = ctx->meta->seq_num;
|
||||
if (seq_num == 0)
|
||||
BPF_SEQ_PRINTF(seq, "Num RefCount Protocol Flags Type St Inode Path\n");
|
||||
|
||||
BPF_SEQ_PRINTF(seq, "%pK: %08X %08X %08X %04X %02X %8lu",
|
||||
unix_sk,
|
||||
sk->sk_refcnt.refs.counter,
|
||||
0,
|
||||
sk->sk_state == TCP_LISTEN ? __SO_ACCEPTCON : 0,
|
||||
sk->sk_type,
|
||||
sk->sk_socket ?
|
||||
(sk->sk_state == TCP_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) :
|
||||
(sk->sk_state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING),
|
||||
sock_i_ino(sk));
|
||||
|
||||
if (unix_sk->addr) {
|
||||
if (!UNIX_ABSTRACT(unix_sk)) {
|
||||
BPF_SEQ_PRINTF(seq, " %s", unix_sk->addr->name->sun_path);
|
||||
} else {
|
||||
/* The name of the abstract UNIX domain socket starts
|
||||
* with '\0' and can contain '\0'. The null bytes
|
||||
* should be escaped as done in unix_seq_show().
|
||||
*/
|
||||
__u64 i, len;
|
||||
|
||||
len = unix_sk->addr->len - sizeof(short);
|
||||
|
||||
BPF_SEQ_PRINTF(seq, " @");
|
||||
|
||||
for (i = 1; i < len; i++) {
|
||||
/* unix_mkname() tests this upper bound. */
|
||||
if (i >= sizeof(struct sockaddr_un))
|
||||
break;
|
||||
|
||||
BPF_SEQ_PRINTF(seq, "%c",
|
||||
unix_sk->addr->name->sun_path[i] ?:
|
||||
'@');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BPF_SEQ_PRINTF(seq, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -5,6 +5,10 @@
|
||||
#define AF_INET 2
|
||||
#define AF_INET6 10
|
||||
|
||||
#define __SO_ACCEPTCON (1 << 16)
|
||||
#define UNIX_HASH_SIZE 256
|
||||
#define UNIX_ABSTRACT(unix_sk) (unix_sk->addr->hash < UNIX_HASH_SIZE)
|
||||
|
||||
#define SOL_TCP 6
|
||||
#define TCP_CONGESTION 13
|
||||
#define TCP_CA_NAME_MAX 16
|
||||
|
@ -59,9 +59,9 @@ int handler(const void *ctx)
|
||||
/* Kernel pointers */
|
||||
addr_ret = BPF_SNPRINTF(addr_out, sizeof(addr_out), "%pK %px %p",
|
||||
0, 0xFFFF00000ADD4E55, 0xFFFF00000ADD4E55);
|
||||
/* Strings embedding */
|
||||
str_ret = BPF_SNPRINTF(str_out, sizeof(str_out), "%s %+05s",
|
||||
str1, longstr);
|
||||
/* Strings and single-byte character embedding */
|
||||
str_ret = BPF_SNPRINTF(str_out, sizeof(str_out), "%s % 9c %+2c %-3c %04c %0c %+05s",
|
||||
str1, 'a', 'b', 'c', 'd', 'e', longstr);
|
||||
/* Overflow */
|
||||
over_ret = BPF_SNPRINTF(over_out, sizeof(over_out), "%%overflow");
|
||||
/* Padding of fixed width numbers */
|
||||
|
Loading…
Reference in New Issue
Block a user