mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-22 20:43:56 +08:00
Merge branch 'bpf_link-observability'
Andrii Nakryiko says: ==================== This patch series adds various observability APIs to bpf_link: - each bpf_link now gets ID, similar to bpf_map and bpf_prog, by which user-space can iterate over all existing bpf_links and create limited FD from ID; - allows to get extra object information with bpf_link general and type-specific information; - implements `bpf link show` command which lists all active bpf_links in the system; - implements `bpf link pin` allowing to pin bpf_link by ID or from other pinned path. v2->v3: - improve spin locking around bpf_link ID (Alexei); - simplify bpf_link_info handling and fix compilation error on sh arch; v1->v2: - simplified `bpftool link show` implementation (Quentin); - fixed formatting of bpftool-link.rst (Quentin); - fixed attach type printing logic (Quentin); rfc->v1: - dropped read-only bpf_links (Alexei); - fixed bug in bpf_link_cleanup() not removing ID; - fixed bpftool link pinning search logic; - added bash-completion and man page. ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
1f427a8077
@ -57,8 +57,6 @@ struct bpf_cgroup_link {
|
||||
enum bpf_attach_type type;
|
||||
};
|
||||
|
||||
extern const struct bpf_link_ops bpf_cgroup_link_lops;
|
||||
|
||||
struct bpf_prog_list {
|
||||
struct list_head node;
|
||||
struct bpf_prog *prog;
|
||||
@ -100,8 +98,6 @@ int __cgroup_bpf_attach(struct cgroup *cgrp,
|
||||
int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
struct bpf_cgroup_link *link,
|
||||
enum bpf_attach_type type);
|
||||
int __cgroup_bpf_replace(struct cgroup *cgrp, struct bpf_cgroup_link *link,
|
||||
struct bpf_prog *new_prog);
|
||||
int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr);
|
||||
|
||||
@ -112,8 +108,6 @@ int cgroup_bpf_attach(struct cgroup *cgrp,
|
||||
u32 flags);
|
||||
int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
enum bpf_attach_type type);
|
||||
int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *old_prog,
|
||||
struct bpf_prog *new_prog);
|
||||
int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr);
|
||||
|
||||
@ -353,7 +347,6 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
#else
|
||||
|
||||
struct bpf_prog;
|
||||
struct bpf_link;
|
||||
struct cgroup_bpf {};
|
||||
static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; }
|
||||
static inline void cgroup_bpf_offline(struct cgroup *cgrp) {}
|
||||
@ -377,13 +370,6 @@ static inline int cgroup_bpf_link_attach(const union bpf_attr *attr,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int cgroup_bpf_replace(struct bpf_link *link,
|
||||
struct bpf_prog *old_prog,
|
||||
struct bpf_prog *new_prog)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
|
@ -1026,9 +1026,11 @@ extern const struct file_operations bpf_prog_fops;
|
||||
extern const struct bpf_verifier_ops _name ## _verifier_ops;
|
||||
#define BPF_MAP_TYPE(_id, _ops) \
|
||||
extern const struct bpf_map_ops _ops;
|
||||
#define BPF_LINK_TYPE(_id, _name)
|
||||
#include <linux/bpf_types.h>
|
||||
#undef BPF_PROG_TYPE
|
||||
#undef BPF_MAP_TYPE
|
||||
#undef BPF_LINK_TYPE
|
||||
|
||||
extern const struct bpf_prog_ops bpf_offload_prog_ops;
|
||||
extern const struct bpf_verifier_ops tc_cls_act_analyzer_ops;
|
||||
@ -1085,21 +1087,35 @@ int bpf_prog_new_fd(struct bpf_prog *prog);
|
||||
|
||||
struct bpf_link {
|
||||
atomic64_t refcnt;
|
||||
u32 id;
|
||||
enum bpf_link_type type;
|
||||
const struct bpf_link_ops *ops;
|
||||
struct bpf_prog *prog;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
struct bpf_link_primer {
|
||||
struct bpf_link *link;
|
||||
struct file *file;
|
||||
int fd;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
struct bpf_link_ops {
|
||||
void (*release)(struct bpf_link *link);
|
||||
void (*dealloc)(struct bpf_link *link);
|
||||
|
||||
int (*update_prog)(struct bpf_link *link, struct bpf_prog *new_prog,
|
||||
struct bpf_prog *old_prog);
|
||||
void (*show_fdinfo)(const struct bpf_link *link, struct seq_file *seq);
|
||||
int (*fill_link_info)(const struct bpf_link *link,
|
||||
struct bpf_link_info *info);
|
||||
};
|
||||
|
||||
void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
|
||||
struct bpf_prog *prog);
|
||||
void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
|
||||
int link_fd);
|
||||
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
|
||||
const struct bpf_link_ops *ops, struct bpf_prog *prog);
|
||||
int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer);
|
||||
int bpf_link_settle(struct bpf_link_primer *primer);
|
||||
void bpf_link_cleanup(struct bpf_link_primer *primer);
|
||||
void bpf_link_inc(struct bpf_link *link);
|
||||
void bpf_link_put(struct bpf_link *link);
|
||||
int bpf_link_new_fd(struct bpf_link *link);
|
||||
|
@ -118,3 +118,9 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_STACK, stack_map_ops)
|
||||
#if defined(CONFIG_BPF_JIT)
|
||||
BPF_MAP_TYPE(BPF_MAP_TYPE_STRUCT_OPS, bpf_struct_ops_map_ops)
|
||||
#endif
|
||||
|
||||
BPF_LINK_TYPE(BPF_LINK_TYPE_RAW_TRACEPOINT, raw_tracepoint)
|
||||
BPF_LINK_TYPE(BPF_LINK_TYPE_TRACING, tracing)
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
BPF_LINK_TYPE(BPF_LINK_TYPE_CGROUP, cgroup)
|
||||
#endif
|
||||
|
@ -113,6 +113,8 @@ enum bpf_cmd {
|
||||
BPF_MAP_DELETE_BATCH,
|
||||
BPF_LINK_CREATE,
|
||||
BPF_LINK_UPDATE,
|
||||
BPF_LINK_GET_FD_BY_ID,
|
||||
BPF_LINK_GET_NEXT_ID,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
@ -220,6 +222,15 @@ enum bpf_attach_type {
|
||||
|
||||
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
|
||||
|
||||
enum bpf_link_type {
|
||||
BPF_LINK_TYPE_UNSPEC = 0,
|
||||
BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
|
||||
BPF_LINK_TYPE_TRACING = 2,
|
||||
BPF_LINK_TYPE_CGROUP = 3,
|
||||
|
||||
MAX_BPF_LINK_TYPE,
|
||||
};
|
||||
|
||||
/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
|
||||
*
|
||||
* NONE(default): No further bpf programs allowed in the subtree.
|
||||
@ -523,6 +534,7 @@ union bpf_attr {
|
||||
__u32 prog_id;
|
||||
__u32 map_id;
|
||||
__u32 btf_id;
|
||||
__u32 link_id;
|
||||
};
|
||||
__u32 next_id;
|
||||
__u32 open_flags;
|
||||
@ -3609,6 +3621,25 @@ struct bpf_btf_info {
|
||||
__u32 id;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_link_info {
|
||||
__u32 type;
|
||||
__u32 id;
|
||||
__u32 prog_id;
|
||||
union {
|
||||
struct {
|
||||
__aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
|
||||
__u32 tp_name_len; /* in/out: tp_name buffer len */
|
||||
} raw_tracepoint;
|
||||
struct {
|
||||
__u32 attach_type;
|
||||
} tracing;
|
||||
struct {
|
||||
__u64 cgroup_id;
|
||||
__u32 attach_type;
|
||||
} cgroup;
|
||||
};
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
|
||||
* by user and intended to be used by socket (e.g. to bind to, depends on
|
||||
* attach attach type).
|
||||
|
@ -3482,6 +3482,7 @@ extern char __weak __stop_BTF[];
|
||||
extern struct btf *btf_vmlinux;
|
||||
|
||||
#define BPF_MAP_TYPE(_id, _ops)
|
||||
#define BPF_LINK_TYPE(_id, _name)
|
||||
static union {
|
||||
struct bpf_ctx_convert {
|
||||
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
|
||||
@ -3508,6 +3509,7 @@ static u8 bpf_ctx_convert_map[] = {
|
||||
0, /* avoid empty array */
|
||||
};
|
||||
#undef BPF_MAP_TYPE
|
||||
#undef BPF_LINK_TYPE
|
||||
|
||||
static const struct btf_member *
|
||||
btf_get_prog_ctx_type(struct bpf_verifier_log *log, struct btf *btf,
|
||||
|
@ -557,8 +557,9 @@ found:
|
||||
*
|
||||
* Must be called with cgroup_mutex held.
|
||||
*/
|
||||
int __cgroup_bpf_replace(struct cgroup *cgrp, struct bpf_cgroup_link *link,
|
||||
struct bpf_prog *new_prog)
|
||||
static int __cgroup_bpf_replace(struct cgroup *cgrp,
|
||||
struct bpf_cgroup_link *link,
|
||||
struct bpf_prog *new_prog)
|
||||
{
|
||||
struct list_head *progs = &cgrp->bpf.progs[link->type];
|
||||
struct bpf_prog *old_prog;
|
||||
@ -583,6 +584,30 @@ int __cgroup_bpf_replace(struct cgroup *cgrp, struct bpf_cgroup_link *link,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *new_prog,
|
||||
struct bpf_prog *old_prog)
|
||||
{
|
||||
struct bpf_cgroup_link *cg_link;
|
||||
int ret;
|
||||
|
||||
cg_link = container_of(link, struct bpf_cgroup_link, link);
|
||||
|
||||
mutex_lock(&cgroup_mutex);
|
||||
/* link might have been auto-released by dying cgroup, so fail */
|
||||
if (!cg_link->cgroup) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (old_prog && link->prog != old_prog) {
|
||||
ret = -EPERM;
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = __cgroup_bpf_replace(cg_link->cgroup, cg_link, new_prog);
|
||||
out_unlock:
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct bpf_prog_list *find_detach_entry(struct list_head *progs,
|
||||
struct bpf_prog *prog,
|
||||
struct bpf_cgroup_link *link,
|
||||
@ -808,17 +833,56 @@ static void bpf_cgroup_link_dealloc(struct bpf_link *link)
|
||||
kfree(cg_link);
|
||||
}
|
||||
|
||||
const struct bpf_link_ops bpf_cgroup_link_lops = {
|
||||
static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link,
|
||||
struct seq_file *seq)
|
||||
{
|
||||
struct bpf_cgroup_link *cg_link =
|
||||
container_of(link, struct bpf_cgroup_link, link);
|
||||
u64 cg_id = 0;
|
||||
|
||||
mutex_lock(&cgroup_mutex);
|
||||
if (cg_link->cgroup)
|
||||
cg_id = cgroup_id(cg_link->cgroup);
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
||||
seq_printf(seq,
|
||||
"cgroup_id:\t%llu\n"
|
||||
"attach_type:\t%d\n",
|
||||
cg_id,
|
||||
cg_link->type);
|
||||
}
|
||||
|
||||
static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link,
|
||||
struct bpf_link_info *info)
|
||||
{
|
||||
struct bpf_cgroup_link *cg_link =
|
||||
container_of(link, struct bpf_cgroup_link, link);
|
||||
u64 cg_id = 0;
|
||||
|
||||
mutex_lock(&cgroup_mutex);
|
||||
if (cg_link->cgroup)
|
||||
cg_id = cgroup_id(cg_link->cgroup);
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
||||
info->cgroup.cgroup_id = cg_id;
|
||||
info->cgroup.attach_type = cg_link->type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bpf_link_ops bpf_cgroup_link_lops = {
|
||||
.release = bpf_cgroup_link_release,
|
||||
.dealloc = bpf_cgroup_link_dealloc,
|
||||
.update_prog = cgroup_bpf_replace,
|
||||
.show_fdinfo = bpf_cgroup_link_show_fdinfo,
|
||||
.fill_link_info = bpf_cgroup_link_fill_link_info,
|
||||
};
|
||||
|
||||
int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_link_primer link_primer;
|
||||
struct bpf_cgroup_link *link;
|
||||
struct file *link_file;
|
||||
struct cgroup *cgrp;
|
||||
int err, link_fd;
|
||||
int err;
|
||||
|
||||
if (attr->link_create.flags)
|
||||
return -EINVAL;
|
||||
@ -832,26 +896,25 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
|
||||
err = -ENOMEM;
|
||||
goto out_put_cgroup;
|
||||
}
|
||||
bpf_link_init(&link->link, &bpf_cgroup_link_lops, prog);
|
||||
bpf_link_init(&link->link, BPF_LINK_TYPE_CGROUP, &bpf_cgroup_link_lops,
|
||||
prog);
|
||||
link->cgroup = cgrp;
|
||||
link->type = attr->link_create.attach_type;
|
||||
|
||||
link_file = bpf_link_new_file(&link->link, &link_fd);
|
||||
if (IS_ERR(link_file)) {
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
if (err) {
|
||||
kfree(link);
|
||||
err = PTR_ERR(link_file);
|
||||
goto out_put_cgroup;
|
||||
}
|
||||
|
||||
err = cgroup_bpf_attach(cgrp, NULL, NULL, link, link->type,
|
||||
BPF_F_ALLOW_MULTI);
|
||||
if (err) {
|
||||
bpf_link_cleanup(&link->link, link_file, link_fd);
|
||||
bpf_link_cleanup(&link_primer);
|
||||
goto out_put_cgroup;
|
||||
}
|
||||
|
||||
fd_install(link_fd, link_file);
|
||||
return link_fd;
|
||||
return bpf_link_settle(&link_primer);
|
||||
|
||||
out_put_cgroup:
|
||||
cgroup_put(cgrp);
|
||||
|
@ -42,6 +42,8 @@ static DEFINE_IDR(prog_idr);
|
||||
static DEFINE_SPINLOCK(prog_idr_lock);
|
||||
static DEFINE_IDR(map_idr);
|
||||
static DEFINE_SPINLOCK(map_idr_lock);
|
||||
static DEFINE_IDR(link_idr);
|
||||
static DEFINE_SPINLOCK(link_idr_lock);
|
||||
|
||||
int sysctl_unprivileged_bpf_disabled __read_mostly;
|
||||
|
||||
@ -49,9 +51,11 @@ static const struct bpf_map_ops * const bpf_map_types[] = {
|
||||
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
|
||||
#define BPF_MAP_TYPE(_id, _ops) \
|
||||
[_id] = &_ops,
|
||||
#define BPF_LINK_TYPE(_id, _name)
|
||||
#include <linux/bpf_types.h>
|
||||
#undef BPF_PROG_TYPE
|
||||
#undef BPF_MAP_TYPE
|
||||
#undef BPF_LINK_TYPE
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1546,9 +1550,11 @@ static const struct bpf_prog_ops * const bpf_prog_types[] = {
|
||||
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
|
||||
[_id] = & _name ## _prog_ops,
|
||||
#define BPF_MAP_TYPE(_id, _ops)
|
||||
#define BPF_LINK_TYPE(_id, _name)
|
||||
#include <linux/bpf_types.h>
|
||||
#undef BPF_PROG_TYPE
|
||||
#undef BPF_MAP_TYPE
|
||||
#undef BPF_LINK_TYPE
|
||||
};
|
||||
|
||||
static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog)
|
||||
@ -2181,25 +2187,39 @@ static int bpf_obj_get(const union bpf_attr *attr)
|
||||
attr->file_flags);
|
||||
}
|
||||
|
||||
void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
|
||||
struct bpf_prog *prog)
|
||||
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
|
||||
const struct bpf_link_ops *ops, struct bpf_prog *prog)
|
||||
{
|
||||
atomic64_set(&link->refcnt, 1);
|
||||
link->type = type;
|
||||
link->id = 0;
|
||||
link->ops = ops;
|
||||
link->prog = prog;
|
||||
}
|
||||
|
||||
static void bpf_link_free_id(int id)
|
||||
{
|
||||
if (!id)
|
||||
return;
|
||||
|
||||
spin_lock_bh(&link_idr_lock);
|
||||
idr_remove(&link_idr, id);
|
||||
spin_unlock_bh(&link_idr_lock);
|
||||
}
|
||||
|
||||
/* Clean up bpf_link and corresponding anon_inode file and FD. After
|
||||
* anon_inode is created, bpf_link can't be just kfree()'d due to deferred
|
||||
* anon_inode's release() call. This helper manages marking bpf_link as
|
||||
* defunct, releases anon_inode file and puts reserved FD.
|
||||
* anon_inode's release() call. This helper marksbpf_link as
|
||||
* defunct, releases anon_inode file and puts reserved FD. bpf_prog's refcnt
|
||||
* is not decremented, it's the responsibility of a calling code that failed
|
||||
* to complete bpf_link initialization.
|
||||
*/
|
||||
void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
|
||||
int link_fd)
|
||||
void bpf_link_cleanup(struct bpf_link_primer *primer)
|
||||
{
|
||||
link->prog = NULL;
|
||||
fput(link_file);
|
||||
put_unused_fd(link_fd);
|
||||
primer->link->prog = NULL;
|
||||
bpf_link_free_id(primer->id);
|
||||
fput(primer->file);
|
||||
put_unused_fd(primer->fd);
|
||||
}
|
||||
|
||||
void bpf_link_inc(struct bpf_link *link)
|
||||
@ -2210,6 +2230,7 @@ void bpf_link_inc(struct bpf_link *link)
|
||||
/* bpf_link_free is guaranteed to be called from process context */
|
||||
static void bpf_link_free(struct bpf_link *link)
|
||||
{
|
||||
bpf_link_free_id(link->id);
|
||||
if (link->prog) {
|
||||
/* detach BPF program, clean up used resources */
|
||||
link->ops->release(link);
|
||||
@ -2250,36 +2271,36 @@ static int bpf_link_release(struct inode *inode, struct file *filp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static const struct bpf_link_ops bpf_raw_tp_lops;
|
||||
static const struct bpf_link_ops bpf_tracing_link_lops;
|
||||
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
|
||||
#define BPF_MAP_TYPE(_id, _ops)
|
||||
#define BPF_LINK_TYPE(_id, _name) [_id] = #_name,
|
||||
static const char *bpf_link_type_strs[] = {
|
||||
[BPF_LINK_TYPE_UNSPEC] = "<invalid>",
|
||||
#include <linux/bpf_types.h>
|
||||
};
|
||||
#undef BPF_PROG_TYPE
|
||||
#undef BPF_MAP_TYPE
|
||||
#undef BPF_LINK_TYPE
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
|
||||
{
|
||||
const struct bpf_link *link = filp->private_data;
|
||||
const struct bpf_prog *prog = link->prog;
|
||||
char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
|
||||
const char *link_type;
|
||||
|
||||
if (link->ops == &bpf_raw_tp_lops)
|
||||
link_type = "raw_tracepoint";
|
||||
else if (link->ops == &bpf_tracing_link_lops)
|
||||
link_type = "tracing";
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
else if (link->ops == &bpf_cgroup_link_lops)
|
||||
link_type = "cgroup";
|
||||
#endif
|
||||
else
|
||||
link_type = "unknown";
|
||||
|
||||
bin2hex(prog_tag, prog->tag, sizeof(prog->tag));
|
||||
seq_printf(m,
|
||||
"link_type:\t%s\n"
|
||||
"link_id:\t%u\n"
|
||||
"prog_tag:\t%s\n"
|
||||
"prog_id:\t%u\n",
|
||||
link_type,
|
||||
bpf_link_type_strs[link->type],
|
||||
link->id,
|
||||
prog_tag,
|
||||
prog->aux->id);
|
||||
if (link->ops->show_fdinfo)
|
||||
link->ops->show_fdinfo(link, m);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2292,36 +2313,76 @@ static const struct file_operations bpf_link_fops = {
|
||||
.write = bpf_dummy_write,
|
||||
};
|
||||
|
||||
int bpf_link_new_fd(struct bpf_link *link)
|
||||
static int bpf_link_alloc_id(struct bpf_link *link)
|
||||
{
|
||||
return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC);
|
||||
int id;
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock_bh(&link_idr_lock);
|
||||
id = idr_alloc_cyclic(&link_idr, link, 1, INT_MAX, GFP_ATOMIC);
|
||||
spin_unlock_bh(&link_idr_lock);
|
||||
idr_preload_end();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Similar to bpf_link_new_fd, create anon_inode for given bpf_link, but
|
||||
* instead of immediately installing fd in fdtable, just reserve it and
|
||||
* return. Caller then need to either install it with fd_install(fd, file) or
|
||||
* release with put_unused_fd(fd).
|
||||
* This is useful for cases when bpf_link attachment/detachment are
|
||||
* complicated and expensive operations and should be delayed until all the fd
|
||||
* reservation and anon_inode creation succeeds.
|
||||
/* Prepare bpf_link to be exposed to user-space by allocating anon_inode file,
|
||||
* reserving unused FD and allocating ID from link_idr. This is to be paired
|
||||
* with bpf_link_settle() to install FD and ID and expose bpf_link to
|
||||
* user-space, if bpf_link is successfully attached. If not, bpf_link and
|
||||
* pre-allocated resources are to be freed with bpf_cleanup() call. All the
|
||||
* transient state is passed around in struct bpf_link_primer.
|
||||
* This is preferred way to create and initialize bpf_link, especially when
|
||||
* there are complicated and expensive operations inbetween creating bpf_link
|
||||
* itself and attaching it to BPF hook. By using bpf_link_prime() and
|
||||
* bpf_link_settle() kernel code using bpf_link doesn't have to perform
|
||||
* expensive (and potentially failing) roll back operations in a rare case
|
||||
* that file, FD, or ID can't be allocated.
|
||||
*/
|
||||
struct file *bpf_link_new_file(struct bpf_link *link, int *reserved_fd)
|
||||
int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer)
|
||||
{
|
||||
struct file *file;
|
||||
int fd;
|
||||
int fd, id;
|
||||
|
||||
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return ERR_PTR(fd);
|
||||
return fd;
|
||||
|
||||
file = anon_inode_getfile("bpf_link", &bpf_link_fops, link, O_CLOEXEC);
|
||||
if (IS_ERR(file)) {
|
||||
put_unused_fd(fd);
|
||||
return file;
|
||||
return PTR_ERR(file);
|
||||
}
|
||||
|
||||
*reserved_fd = fd;
|
||||
return file;
|
||||
id = bpf_link_alloc_id(link);
|
||||
if (id < 0) {
|
||||
put_unused_fd(fd);
|
||||
fput(file);
|
||||
return id;
|
||||
}
|
||||
|
||||
primer->link = link;
|
||||
primer->file = file;
|
||||
primer->fd = fd;
|
||||
primer->id = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_link_settle(struct bpf_link_primer *primer)
|
||||
{
|
||||
/* make bpf_link fetchable by ID */
|
||||
spin_lock_bh(&link_idr_lock);
|
||||
primer->link->id = primer->id;
|
||||
spin_unlock_bh(&link_idr_lock);
|
||||
/* make bpf_link fetchable by FD */
|
||||
fd_install(primer->fd, primer->file);
|
||||
/* pass through installed FD */
|
||||
return primer->fd;
|
||||
}
|
||||
|
||||
int bpf_link_new_fd(struct bpf_link *link)
|
||||
{
|
||||
return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC);
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_link_get_from_fd(u32 ufd)
|
||||
@ -2345,6 +2406,7 @@ struct bpf_link *bpf_link_get_from_fd(u32 ufd)
|
||||
|
||||
struct bpf_tracing_link {
|
||||
struct bpf_link link;
|
||||
enum bpf_attach_type attach_type;
|
||||
};
|
||||
|
||||
static void bpf_tracing_link_release(struct bpf_link *link)
|
||||
@ -2360,16 +2422,40 @@ static void bpf_tracing_link_dealloc(struct bpf_link *link)
|
||||
kfree(tr_link);
|
||||
}
|
||||
|
||||
static void bpf_tracing_link_show_fdinfo(const struct bpf_link *link,
|
||||
struct seq_file *seq)
|
||||
{
|
||||
struct bpf_tracing_link *tr_link =
|
||||
container_of(link, struct bpf_tracing_link, link);
|
||||
|
||||
seq_printf(seq,
|
||||
"attach_type:\t%d\n",
|
||||
tr_link->attach_type);
|
||||
}
|
||||
|
||||
static int bpf_tracing_link_fill_link_info(const struct bpf_link *link,
|
||||
struct bpf_link_info *info)
|
||||
{
|
||||
struct bpf_tracing_link *tr_link =
|
||||
container_of(link, struct bpf_tracing_link, link);
|
||||
|
||||
info->tracing.attach_type = tr_link->attach_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bpf_link_ops bpf_tracing_link_lops = {
|
||||
.release = bpf_tracing_link_release,
|
||||
.dealloc = bpf_tracing_link_dealloc,
|
||||
.show_fdinfo = bpf_tracing_link_show_fdinfo,
|
||||
.fill_link_info = bpf_tracing_link_fill_link_info,
|
||||
};
|
||||
|
||||
static int bpf_tracing_prog_attach(struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_link_primer link_primer;
|
||||
struct bpf_tracing_link *link;
|
||||
struct file *link_file;
|
||||
int link_fd, err;
|
||||
int err;
|
||||
|
||||
switch (prog->type) {
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
@ -2402,24 +2488,23 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
|
||||
err = -ENOMEM;
|
||||
goto out_put_prog;
|
||||
}
|
||||
bpf_link_init(&link->link, &bpf_tracing_link_lops, prog);
|
||||
bpf_link_init(&link->link, BPF_LINK_TYPE_TRACING,
|
||||
&bpf_tracing_link_lops, prog);
|
||||
link->attach_type = prog->expected_attach_type;
|
||||
|
||||
link_file = bpf_link_new_file(&link->link, &link_fd);
|
||||
if (IS_ERR(link_file)) {
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
if (err) {
|
||||
kfree(link);
|
||||
err = PTR_ERR(link_file);
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
err = bpf_trampoline_link_prog(prog);
|
||||
if (err) {
|
||||
bpf_link_cleanup(&link->link, link_file, link_fd);
|
||||
bpf_link_cleanup(&link_primer);
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
fd_install(link_fd, link_file);
|
||||
return link_fd;
|
||||
|
||||
return bpf_link_settle(&link_primer);
|
||||
out_put_prog:
|
||||
bpf_prog_put(prog);
|
||||
return err;
|
||||
@ -2447,22 +2532,69 @@ static void bpf_raw_tp_link_dealloc(struct bpf_link *link)
|
||||
kfree(raw_tp);
|
||||
}
|
||||
|
||||
static const struct bpf_link_ops bpf_raw_tp_lops = {
|
||||
static void bpf_raw_tp_link_show_fdinfo(const struct bpf_link *link,
|
||||
struct seq_file *seq)
|
||||
{
|
||||
struct bpf_raw_tp_link *raw_tp_link =
|
||||
container_of(link, struct bpf_raw_tp_link, link);
|
||||
|
||||
seq_printf(seq,
|
||||
"tp_name:\t%s\n",
|
||||
raw_tp_link->btp->tp->name);
|
||||
}
|
||||
|
||||
static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link,
|
||||
struct bpf_link_info *info)
|
||||
{
|
||||
struct bpf_raw_tp_link *raw_tp_link =
|
||||
container_of(link, struct bpf_raw_tp_link, link);
|
||||
char __user *ubuf = u64_to_user_ptr(info->raw_tracepoint.tp_name);
|
||||
const char *tp_name = raw_tp_link->btp->tp->name;
|
||||
u32 ulen = info->raw_tracepoint.tp_name_len;
|
||||
size_t tp_len = strlen(tp_name);
|
||||
|
||||
if (ulen && !ubuf)
|
||||
return -EINVAL;
|
||||
|
||||
info->raw_tracepoint.tp_name_len = tp_len + 1;
|
||||
|
||||
if (!ubuf)
|
||||
return 0;
|
||||
|
||||
if (ulen >= tp_len + 1) {
|
||||
if (copy_to_user(ubuf, tp_name, tp_len + 1))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
char zero = '\0';
|
||||
|
||||
if (copy_to_user(ubuf, tp_name, ulen - 1))
|
||||
return -EFAULT;
|
||||
if (put_user(zero, ubuf + ulen - 1))
|
||||
return -EFAULT;
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bpf_link_ops bpf_raw_tp_link_lops = {
|
||||
.release = bpf_raw_tp_link_release,
|
||||
.dealloc = bpf_raw_tp_link_dealloc,
|
||||
.show_fdinfo = bpf_raw_tp_link_show_fdinfo,
|
||||
.fill_link_info = bpf_raw_tp_link_fill_link_info,
|
||||
};
|
||||
|
||||
#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
|
||||
|
||||
static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_link_primer link_primer;
|
||||
struct bpf_raw_tp_link *link;
|
||||
struct bpf_raw_event_map *btp;
|
||||
struct file *link_file;
|
||||
struct bpf_prog *prog;
|
||||
const char *tp_name;
|
||||
char buf[128];
|
||||
int link_fd, err;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN))
|
||||
return -EINVAL;
|
||||
@ -2515,24 +2647,23 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
err = -ENOMEM;
|
||||
goto out_put_btp;
|
||||
}
|
||||
bpf_link_init(&link->link, &bpf_raw_tp_lops, prog);
|
||||
bpf_link_init(&link->link, BPF_LINK_TYPE_RAW_TRACEPOINT,
|
||||
&bpf_raw_tp_link_lops, prog);
|
||||
link->btp = btp;
|
||||
|
||||
link_file = bpf_link_new_file(&link->link, &link_fd);
|
||||
if (IS_ERR(link_file)) {
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
if (err) {
|
||||
kfree(link);
|
||||
err = PTR_ERR(link_file);
|
||||
goto out_put_btp;
|
||||
}
|
||||
|
||||
err = bpf_probe_register(link->btp, prog);
|
||||
if (err) {
|
||||
bpf_link_cleanup(&link->link, link_file, link_fd);
|
||||
bpf_link_cleanup(&link_primer);
|
||||
goto out_put_btp;
|
||||
}
|
||||
|
||||
fd_install(link_fd, link_file);
|
||||
return link_fd;
|
||||
return bpf_link_settle(&link_primer);
|
||||
|
||||
out_put_btp:
|
||||
bpf_put_raw_tracepoint(btp);
|
||||
@ -3313,6 +3444,42 @@ static int bpf_btf_get_info_by_fd(struct btf *btf,
|
||||
return btf_get_info_by_fd(btf, attr, uattr);
|
||||
}
|
||||
|
||||
static int bpf_link_get_info_by_fd(struct bpf_link *link,
|
||||
const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
struct bpf_link_info __user *uinfo = u64_to_user_ptr(attr->info.info);
|
||||
struct bpf_link_info info;
|
||||
u32 info_len = attr->info.info_len;
|
||||
int err;
|
||||
|
||||
err = bpf_check_uarg_tail_zero(uinfo, sizeof(info), info_len);
|
||||
if (err)
|
||||
return err;
|
||||
info_len = min_t(u32, sizeof(info), info_len);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
if (copy_from_user(&info, uinfo, info_len))
|
||||
return -EFAULT;
|
||||
|
||||
info.type = link->type;
|
||||
info.id = link->id;
|
||||
info.prog_id = link->prog->aux->id;
|
||||
|
||||
if (link->ops->fill_link_info) {
|
||||
err = link->ops->fill_link_info(link, &info);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (copy_to_user(uinfo, &info, info_len) ||
|
||||
put_user(info_len, &uattr->info.info_len))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define BPF_OBJ_GET_INFO_BY_FD_LAST_FIELD info.info
|
||||
|
||||
static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
|
||||
@ -3337,6 +3504,9 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
|
||||
uattr);
|
||||
else if (f.file->f_op == &btf_fops)
|
||||
err = bpf_btf_get_info_by_fd(f.file->private_data, attr, uattr);
|
||||
else if (f.file->f_op == &bpf_link_fops)
|
||||
err = bpf_link_get_info_by_fd(f.file->private_data,
|
||||
attr, uattr);
|
||||
else
|
||||
err = -EINVAL;
|
||||
|
||||
@ -3464,7 +3634,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
|
||||
if (file->f_op == &bpf_link_fops) {
|
||||
struct bpf_link *link = file->private_data;
|
||||
|
||||
if (link->ops == &bpf_raw_tp_lops) {
|
||||
if (link->ops == &bpf_raw_tp_link_lops) {
|
||||
struct bpf_raw_tp_link *raw_tp =
|
||||
container_of(link, struct bpf_raw_tp_link, link);
|
||||
struct bpf_raw_event_map *btp = raw_tp->btp;
|
||||
@ -3645,13 +3815,10 @@ static int link_update(union bpf_attr *attr)
|
||||
goto out_put_progs;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
if (link->ops == &bpf_cgroup_link_lops) {
|
||||
ret = cgroup_bpf_replace(link, old_prog, new_prog);
|
||||
goto out_put_progs;
|
||||
}
|
||||
#endif
|
||||
ret = -EINVAL;
|
||||
if (link->ops->update_prog)
|
||||
ret = link->ops->update_prog(link, new_prog, old_prog);
|
||||
else
|
||||
ret = EINVAL;
|
||||
|
||||
out_put_progs:
|
||||
if (old_prog)
|
||||
@ -3663,6 +3830,48 @@ out_put_link:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bpf_link_inc_not_zero(struct bpf_link *link)
|
||||
{
|
||||
return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
#define BPF_LINK_GET_FD_BY_ID_LAST_FIELD link_id
|
||||
|
||||
static int bpf_link_get_fd_by_id(const union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_link *link;
|
||||
u32 id = attr->link_id;
|
||||
int fd, err;
|
||||
|
||||
if (CHECK_ATTR(BPF_LINK_GET_FD_BY_ID))
|
||||
return -EINVAL;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
spin_lock_bh(&link_idr_lock);
|
||||
link = idr_find(&link_idr, id);
|
||||
/* before link is "settled", ID is 0, pretend it doesn't exist yet */
|
||||
if (link) {
|
||||
if (link->id)
|
||||
err = bpf_link_inc_not_zero(link);
|
||||
else
|
||||
err = -EAGAIN;
|
||||
} else {
|
||||
err = -ENOENT;
|
||||
}
|
||||
spin_unlock_bh(&link_idr_lock);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fd = bpf_link_new_fd(link);
|
||||
if (fd < 0)
|
||||
bpf_link_put(link);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
@ -3780,6 +3989,13 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
||||
case BPF_LINK_UPDATE:
|
||||
err = link_update(&attr);
|
||||
break;
|
||||
case BPF_LINK_GET_FD_BY_ID:
|
||||
err = bpf_link_get_fd_by_id(&attr);
|
||||
break;
|
||||
case BPF_LINK_GET_NEXT_ID:
|
||||
err = bpf_obj_get_next_id(&attr, uattr,
|
||||
&link_idr, &link_idr_lock);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
|
@ -28,9 +28,11 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
|
||||
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
|
||||
[_id] = & _name ## _verifier_ops,
|
||||
#define BPF_MAP_TYPE(_id, _ops)
|
||||
#define BPF_LINK_TYPE(_id, _name)
|
||||
#include <linux/bpf_types.h>
|
||||
#undef BPF_PROG_TYPE
|
||||
#undef BPF_MAP_TYPE
|
||||
#undef BPF_LINK_TYPE
|
||||
};
|
||||
|
||||
/* bpf_check() is a static code analyzer that walks eBPF program
|
||||
|
@ -6508,33 +6508,6 @@ int cgroup_bpf_attach(struct cgroup *cgrp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *old_prog,
|
||||
struct bpf_prog *new_prog)
|
||||
{
|
||||
struct bpf_cgroup_link *cg_link;
|
||||
int ret;
|
||||
|
||||
if (link->ops != &bpf_cgroup_link_lops)
|
||||
return -EINVAL;
|
||||
|
||||
cg_link = container_of(link, struct bpf_cgroup_link, link);
|
||||
|
||||
mutex_lock(&cgroup_mutex);
|
||||
/* link might have been auto-released by dying cgroup, so fail */
|
||||
if (!cg_link->cgroup) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (old_prog && link->prog != old_prog) {
|
||||
ret = -EPERM;
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = __cgroup_bpf_replace(cg_link->cgroup, cg_link, new_prog);
|
||||
out_unlock:
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
enum bpf_attach_type type)
|
||||
{
|
||||
|
118
tools/bpf/bpftool/Documentation/bpftool-link.rst
Normal file
118
tools/bpf/bpftool/Documentation/bpftool-link.rst
Normal file
@ -0,0 +1,118 @@
|
||||
================
|
||||
bpftool-link
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF links
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **link** *COMMAND*
|
||||
|
||||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
|
||||
|
||||
*COMMANDS* := { **show** | **list** | **pin** | **help** }
|
||||
|
||||
LINK COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **link { show | list }** [*LINK*]
|
||||
| **bpftool** **link pin** *LINK* *FILE*
|
||||
| **bpftool** **link help**
|
||||
|
|
||||
| *LINK* := { **id** *LINK_ID* | **pinned** *FILE* }
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool link { show | list }** [*LINK*]
|
||||
Show information about active links. If *LINK* is
|
||||
specified show information only about given link,
|
||||
otherwise list all links currently active on the system.
|
||||
|
||||
Output will start with link ID followed by link type and
|
||||
zero or more named attributes, some of which depend on type
|
||||
of link.
|
||||
|
||||
**bpftool link pin** *LINK* *FILE*
|
||||
Pin link *LINK* as *FILE*.
|
||||
|
||||
Note: *FILE* must be located in *bpffs* mount. It must not
|
||||
contain a dot character ('.'), which is reserved for future
|
||||
extensions of *bpffs*.
|
||||
|
||||
**bpftool link help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
-h, --help
|
||||
Print short generic help message (similar to **bpftool help**).
|
||||
|
||||
-V, --version
|
||||
Print version number (similar to **bpftool version**).
|
||||
|
||||
-j, --json
|
||||
Generate JSON output. For commands that cannot produce JSON, this
|
||||
option has no effect.
|
||||
|
||||
-p, --pretty
|
||||
Generate human-readable JSON output. Implies **-j**.
|
||||
|
||||
-f, --bpffs
|
||||
When showing BPF links, show file names of pinned
|
||||
links.
|
||||
|
||||
-n, --nomount
|
||||
Do not automatically attempt to mount any virtual file system
|
||||
(such as tracefs or BPF virtual file system) when necessary.
|
||||
|
||||
-d, --debug
|
||||
Print all logs available, even debug-level information. This
|
||||
includes logs from libbpf.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool link show**
|
||||
|
||||
::
|
||||
|
||||
10: cgroup prog 25
|
||||
cgroup_id 614 attach_type egress
|
||||
|
||||
**# bpftool --json --pretty link show**
|
||||
|
||||
::
|
||||
|
||||
[{
|
||||
"type": "cgroup",
|
||||
"prog_id": 25,
|
||||
"cgroup_id": 614,
|
||||
"attach_type": "egress"
|
||||
}
|
||||
]
|
||||
|
||||
|
|
||||
| **# bpftool link pin id 10 /sys/fs/bpf/link**
|
||||
| **# ls -l /sys/fs/bpf/**
|
||||
|
||||
::
|
||||
|
||||
-rw------- 1 root root 0 Apr 23 21:39 link
|
||||
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpf**\ (2),
|
||||
**bpf-helpers**\ (7),
|
||||
**bpftool**\ (8),
|
||||
**bpftool-prog\ (8),
|
||||
**bpftool-map**\ (8),
|
||||
**bpftool-cgroup**\ (8),
|
||||
**bpftool-feature**\ (8),
|
||||
**bpftool-net**\ (8),
|
||||
**bpftool-perf**\ (8),
|
||||
**bpftool-btf**\ (8)
|
@ -98,6 +98,12 @@ _bpftool_get_btf_ids()
|
||||
command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_bpftool_get_link_ids()
|
||||
{
|
||||
COMPREPLY+=( $( compgen -W "$( bpftool -jp link 2>&1 | \
|
||||
command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_bpftool_get_obj_map_names()
|
||||
{
|
||||
local obj
|
||||
@ -1082,6 +1088,39 @@ _bpftool()
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
link)
|
||||
case $command in
|
||||
show|list|pin)
|
||||
case $prev in
|
||||
id)
|
||||
_bpftool_get_link_ids
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
local LINK_TYPE='id pinned'
|
||||
case $command in
|
||||
show|list)
|
||||
[[ $prev != "$command" ]] && return 0
|
||||
COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
pin)
|
||||
if [[ $prev == "$command" ]]; then
|
||||
COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
[[ $prev == $object ]] && \
|
||||
COMPREPLY=( $( compgen -W 'help pin show list' -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
} &&
|
||||
complete -F _bpftool bpftool
|
||||
|
@ -31,42 +31,20 @@
|
||||
|
||||
static unsigned int query_flags;
|
||||
|
||||
static const char * const attach_type_strings[] = {
|
||||
[BPF_CGROUP_INET_INGRESS] = "ingress",
|
||||
[BPF_CGROUP_INET_EGRESS] = "egress",
|
||||
[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
|
||||
[BPF_CGROUP_SOCK_OPS] = "sock_ops",
|
||||
[BPF_CGROUP_DEVICE] = "device",
|
||||
[BPF_CGROUP_INET4_BIND] = "bind4",
|
||||
[BPF_CGROUP_INET6_BIND] = "bind6",
|
||||
[BPF_CGROUP_INET4_CONNECT] = "connect4",
|
||||
[BPF_CGROUP_INET6_CONNECT] = "connect6",
|
||||
[BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
|
||||
[BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
|
||||
[BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
|
||||
[BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
|
||||
[BPF_CGROUP_SYSCTL] = "sysctl",
|
||||
[BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
|
||||
[BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
|
||||
[BPF_CGROUP_GETSOCKOPT] = "getsockopt",
|
||||
[BPF_CGROUP_SETSOCKOPT] = "setsockopt",
|
||||
[__MAX_BPF_ATTACH_TYPE] = NULL,
|
||||
};
|
||||
|
||||
static enum bpf_attach_type parse_attach_type(const char *str)
|
||||
{
|
||||
enum bpf_attach_type type;
|
||||
|
||||
for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
|
||||
if (attach_type_strings[type] &&
|
||||
is_prefix(str, attach_type_strings[type]))
|
||||
if (attach_type_name[type] &&
|
||||
is_prefix(str, attach_type_name[type]))
|
||||
return type;
|
||||
}
|
||||
|
||||
return __MAX_BPF_ATTACH_TYPE;
|
||||
}
|
||||
|
||||
static int show_bpf_prog(int id, const char *attach_type_str,
|
||||
static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
|
||||
const char *attach_flags_str,
|
||||
int level)
|
||||
{
|
||||
@ -86,18 +64,22 @@ static int show_bpf_prog(int id, const char *attach_type_str,
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_uint_field(json_wtr, "id", info.id);
|
||||
jsonw_string_field(json_wtr, "attach_type",
|
||||
attach_type_str);
|
||||
if (attach_type < ARRAY_SIZE(attach_type_name))
|
||||
jsonw_string_field(json_wtr, "attach_type",
|
||||
attach_type_name[attach_type]);
|
||||
else
|
||||
jsonw_uint_field(json_wtr, "attach_type", attach_type);
|
||||
jsonw_string_field(json_wtr, "attach_flags",
|
||||
attach_flags_str);
|
||||
jsonw_string_field(json_wtr, "name", info.name);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
printf("%s%-8u %-15s %-15s %-15s\n", level ? " " : "",
|
||||
info.id,
|
||||
attach_type_str,
|
||||
attach_flags_str,
|
||||
info.name);
|
||||
printf("%s%-8u ", level ? " " : "", info.id);
|
||||
if (attach_type < ARRAY_SIZE(attach_type_name))
|
||||
printf("%-15s", attach_type_name[attach_type]);
|
||||
else
|
||||
printf("type %-10u", attach_type);
|
||||
printf(" %-15s %-15s\n", attach_flags_str, info.name);
|
||||
}
|
||||
|
||||
close(prog_fd);
|
||||
@ -171,7 +153,7 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
|
||||
}
|
||||
|
||||
for (iter = 0; iter < prog_cnt; iter++)
|
||||
show_bpf_prog(prog_ids[iter], attach_type_strings[type],
|
||||
show_bpf_prog(prog_ids[iter], type,
|
||||
attach_flags_str, level);
|
||||
|
||||
return 0;
|
||||
|
@ -262,6 +262,8 @@ int get_fd_type(int fd)
|
||||
return BPF_OBJ_MAP;
|
||||
else if (strstr(buf, "bpf-prog"))
|
||||
return BPF_OBJ_PROG;
|
||||
else if (strstr(buf, "bpf-link"))
|
||||
return BPF_OBJ_LINK;
|
||||
|
||||
return BPF_OBJ_UNKNOWN;
|
||||
}
|
||||
|
333
tools/bpf/bpftool/link.c
Normal file
333
tools/bpf/bpftool/link.c
Normal file
@ -0,0 +1,333 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
/* Copyright (C) 2020 Facebook */
|
||||
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
#include "json_writer.h"
|
||||
#include "main.h"
|
||||
|
||||
static const char * const link_type_name[] = {
|
||||
[BPF_LINK_TYPE_UNSPEC] = "unspec",
|
||||
[BPF_LINK_TYPE_RAW_TRACEPOINT] = "raw_tracepoint",
|
||||
[BPF_LINK_TYPE_TRACING] = "tracing",
|
||||
[BPF_LINK_TYPE_CGROUP] = "cgroup",
|
||||
};
|
||||
|
||||
static int link_parse_fd(int *argc, char ***argv)
|
||||
{
|
||||
if (is_prefix(**argv, "id")) {
|
||||
unsigned int id;
|
||||
char *endptr;
|
||||
|
||||
NEXT_ARGP();
|
||||
|
||||
id = strtoul(**argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
p_err("can't parse %s as ID", **argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
return bpf_link_get_fd_by_id(id);
|
||||
} else if (is_prefix(**argv, "pinned")) {
|
||||
char *path;
|
||||
|
||||
NEXT_ARGP();
|
||||
|
||||
path = **argv;
|
||||
NEXT_ARGP();
|
||||
|
||||
return open_obj_pinned_any(path, BPF_OBJ_LINK);
|
||||
}
|
||||
|
||||
p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
|
||||
{
|
||||
jsonw_uint_field(wtr, "id", info->id);
|
||||
if (info->type < ARRAY_SIZE(link_type_name))
|
||||
jsonw_string_field(wtr, "type", link_type_name[info->type]);
|
||||
else
|
||||
jsonw_uint_field(wtr, "type", info->type);
|
||||
|
||||
jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
|
||||
}
|
||||
|
||||
static int get_prog_info(int prog_id, struct bpf_prog_info *info)
|
||||
{
|
||||
__u32 len = sizeof(*info);
|
||||
int err, prog_fd;
|
||||
|
||||
prog_fd = bpf_prog_get_fd_by_id(prog_id);
|
||||
if (prog_fd < 0)
|
||||
return prog_fd;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
err = bpf_obj_get_info_by_fd(prog_fd, info, &len);
|
||||
if (err)
|
||||
p_err("can't get prog info: %s", strerror(errno));
|
||||
close(prog_fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int show_link_close_json(int fd, struct bpf_link_info *info)
|
||||
{
|
||||
struct bpf_prog_info prog_info;
|
||||
int err;
|
||||
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
show_link_header_json(info, json_wtr);
|
||||
|
||||
switch (info->type) {
|
||||
case BPF_LINK_TYPE_RAW_TRACEPOINT:
|
||||
jsonw_string_field(json_wtr, "tp_name",
|
||||
(const char *)info->raw_tracepoint.tp_name);
|
||||
break;
|
||||
case BPF_LINK_TYPE_TRACING:
|
||||
err = get_prog_info(info->prog_id, &prog_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (prog_info.type < ARRAY_SIZE(prog_type_name))
|
||||
jsonw_string_field(json_wtr, "prog_type",
|
||||
prog_type_name[prog_info.type]);
|
||||
else
|
||||
jsonw_uint_field(json_wtr, "prog_type",
|
||||
prog_info.type);
|
||||
|
||||
if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name))
|
||||
jsonw_string_field(json_wtr, "attach_type",
|
||||
attach_type_name[info->tracing.attach_type]);
|
||||
else
|
||||
jsonw_uint_field(json_wtr, "attach_type",
|
||||
info->tracing.attach_type);
|
||||
break;
|
||||
case BPF_LINK_TYPE_CGROUP:
|
||||
jsonw_lluint_field(json_wtr, "cgroup_id",
|
||||
info->cgroup.cgroup_id);
|
||||
if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name))
|
||||
jsonw_string_field(json_wtr, "attach_type",
|
||||
attach_type_name[info->cgroup.attach_type]);
|
||||
else
|
||||
jsonw_uint_field(json_wtr, "attach_type",
|
||||
info->cgroup.attach_type);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hash_empty(link_table.table)) {
|
||||
struct pinned_obj *obj;
|
||||
|
||||
jsonw_name(json_wtr, "pinned");
|
||||
jsonw_start_array(json_wtr);
|
||||
hash_for_each_possible(link_table.table, obj, hash, info->id) {
|
||||
if (obj->id == info->id)
|
||||
jsonw_string(json_wtr, obj->path);
|
||||
}
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
jsonw_end_object(json_wtr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_link_header_plain(struct bpf_link_info *info)
|
||||
{
|
||||
printf("%u: ", info->id);
|
||||
if (info->type < ARRAY_SIZE(link_type_name))
|
||||
printf("%s ", link_type_name[info->type]);
|
||||
else
|
||||
printf("type %u ", info->type);
|
||||
|
||||
printf("prog %u ", info->prog_id);
|
||||
}
|
||||
|
||||
static int show_link_close_plain(int fd, struct bpf_link_info *info)
|
||||
{
|
||||
struct bpf_prog_info prog_info;
|
||||
int err;
|
||||
|
||||
show_link_header_plain(info);
|
||||
|
||||
switch (info->type) {
|
||||
case BPF_LINK_TYPE_RAW_TRACEPOINT:
|
||||
printf("\n\ttp '%s' ",
|
||||
(const char *)info->raw_tracepoint.tp_name);
|
||||
break;
|
||||
case BPF_LINK_TYPE_TRACING:
|
||||
err = get_prog_info(info->prog_id, &prog_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (prog_info.type < ARRAY_SIZE(prog_type_name))
|
||||
printf("\n\tprog_type %s ",
|
||||
prog_type_name[prog_info.type]);
|
||||
else
|
||||
printf("\n\tprog_type %u ", prog_info.type);
|
||||
|
||||
if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name))
|
||||
printf("attach_type %s ",
|
||||
attach_type_name[info->tracing.attach_type]);
|
||||
else
|
||||
printf("attach_type %u ", info->tracing.attach_type);
|
||||
break;
|
||||
case BPF_LINK_TYPE_CGROUP:
|
||||
printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id);
|
||||
if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name))
|
||||
printf("attach_type %s ",
|
||||
attach_type_name[info->cgroup.attach_type]);
|
||||
else
|
||||
printf("attach_type %u ", info->cgroup.attach_type);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hash_empty(link_table.table)) {
|
||||
struct pinned_obj *obj;
|
||||
|
||||
hash_for_each_possible(link_table.table, obj, hash, info->id) {
|
||||
if (obj->id == info->id)
|
||||
printf("\n\tpinned %s", obj->path);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show_link(int fd)
|
||||
{
|
||||
struct bpf_link_info info;
|
||||
__u32 len = sizeof(info);
|
||||
char raw_tp_name[256];
|
||||
int err;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
again:
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get link info: %s",
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
|
||||
!info.raw_tracepoint.tp_name) {
|
||||
info.raw_tracepoint.tp_name = (unsigned long)&raw_tp_name;
|
||||
info.raw_tracepoint.tp_name_len = sizeof(raw_tp_name);
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
show_link_close_json(fd, &info);
|
||||
else
|
||||
show_link_close_plain(fd, &info);
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show(int argc, char **argv)
|
||||
{
|
||||
__u32 id = 0;
|
||||
int err, fd;
|
||||
|
||||
if (show_pinned)
|
||||
build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
|
||||
|
||||
if (argc == 2) {
|
||||
fd = link_parse_fd(&argc, &argv);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
return do_show_link(fd);
|
||||
}
|
||||
|
||||
if (argc)
|
||||
return BAD_ARG();
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (true) {
|
||||
err = bpf_link_get_next_id(id, &id);
|
||||
if (err) {
|
||||
if (errno == ENOENT)
|
||||
break;
|
||||
p_err("can't get next link: %s%s", strerror(errno),
|
||||
errno == EINVAL ? " -- kernel too old?" : "");
|
||||
break;
|
||||
}
|
||||
|
||||
fd = bpf_link_get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
p_err("can't get link by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
err = do_show_link(fd);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
return errno == ENOENT ? 0 : -1;
|
||||
}
|
||||
|
||||
static int do_pin(int argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = do_pin_any(argc, argv, link_parse_fd);
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %1$s %2$s { show | list } [LINK]\n"
|
||||
" %1$s %2$s pin LINK FILE\n"
|
||||
" %1$s %2$s help\n"
|
||||
"\n"
|
||||
" " HELP_SPEC_LINK "\n"
|
||||
" " HELP_SPEC_PROGRAM "\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, argv[-2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cmd cmds[] = {
|
||||
{ "show", do_show },
|
||||
{ "list", do_show },
|
||||
{ "help", do_help },
|
||||
{ "pin", do_pin },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
int do_link(int argc, char **argv)
|
||||
{
|
||||
return cmd_select(cmds, argc, argv, do_help);
|
||||
}
|
@ -30,6 +30,7 @@ bool verifier_logs;
|
||||
bool relaxed_maps;
|
||||
struct pinned_obj_table prog_table;
|
||||
struct pinned_obj_table map_table;
|
||||
struct pinned_obj_table link_table;
|
||||
|
||||
static void __noreturn clean_and_exit(int i)
|
||||
{
|
||||
@ -58,7 +59,7 @@ static int do_help(int argc, char **argv)
|
||||
" %s batch file FILE\n"
|
||||
" %s version\n"
|
||||
"\n"
|
||||
" OBJECT := { prog | map | cgroup | perf | net | feature | btf | gen | struct_ops }\n"
|
||||
" OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops }\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, bin_name, bin_name);
|
||||
@ -215,6 +216,7 @@ static const struct cmd cmds[] = {
|
||||
{ "batch", do_batch },
|
||||
{ "prog", do_prog },
|
||||
{ "map", do_map },
|
||||
{ "link", do_link },
|
||||
{ "cgroup", do_cgroup },
|
||||
{ "perf", do_perf },
|
||||
{ "net", do_net },
|
||||
@ -364,6 +366,7 @@ int main(int argc, char **argv)
|
||||
|
||||
hash_init(prog_table.table);
|
||||
hash_init(map_table.table);
|
||||
hash_init(link_table.table);
|
||||
|
||||
opterr = 0;
|
||||
while ((opt = getopt_long(argc, argv, "Vhpjfmnd",
|
||||
@ -422,6 +425,7 @@ int main(int argc, char **argv)
|
||||
if (show_pinned) {
|
||||
delete_pinned_obj_table(&prog_table);
|
||||
delete_pinned_obj_table(&map_table);
|
||||
delete_pinned_obj_table(&link_table);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -50,6 +50,8 @@
|
||||
"\t {-m|--mapcompat} | {-n|--nomount} }"
|
||||
#define HELP_SPEC_MAP \
|
||||
"MAP := { id MAP_ID | pinned FILE | name MAP_NAME }"
|
||||
#define HELP_SPEC_LINK \
|
||||
"LINK := { id LINK_ID | pinned FILE }"
|
||||
|
||||
static const char * const prog_type_name[] = {
|
||||
[BPF_PROG_TYPE_UNSPEC] = "unspec",
|
||||
@ -83,6 +85,38 @@ static const char * const prog_type_name[] = {
|
||||
[BPF_PROG_TYPE_EXT] = "ext",
|
||||
};
|
||||
|
||||
static const char * const attach_type_name[__MAX_BPF_ATTACH_TYPE] = {
|
||||
[BPF_CGROUP_INET_INGRESS] = "ingress",
|
||||
[BPF_CGROUP_INET_EGRESS] = "egress",
|
||||
[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
|
||||
[BPF_CGROUP_SOCK_OPS] = "sock_ops",
|
||||
[BPF_CGROUP_DEVICE] = "device",
|
||||
[BPF_CGROUP_INET4_BIND] = "bind4",
|
||||
[BPF_CGROUP_INET6_BIND] = "bind6",
|
||||
[BPF_CGROUP_INET4_CONNECT] = "connect4",
|
||||
[BPF_CGROUP_INET6_CONNECT] = "connect6",
|
||||
[BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
|
||||
[BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
|
||||
[BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
|
||||
[BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
|
||||
[BPF_CGROUP_SYSCTL] = "sysctl",
|
||||
[BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
|
||||
[BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
|
||||
[BPF_CGROUP_GETSOCKOPT] = "getsockopt",
|
||||
[BPF_CGROUP_SETSOCKOPT] = "setsockopt",
|
||||
|
||||
[BPF_SK_SKB_STREAM_PARSER] = "sk_skb_stream_parser",
|
||||
[BPF_SK_SKB_STREAM_VERDICT] = "sk_skb_stream_verdict",
|
||||
[BPF_SK_MSG_VERDICT] = "sk_msg_verdict",
|
||||
[BPF_LIRC_MODE2] = "lirc_mode2",
|
||||
[BPF_FLOW_DISSECTOR] = "flow_dissector",
|
||||
[BPF_TRACE_RAW_TP] = "raw_tp",
|
||||
[BPF_TRACE_FENTRY] = "fentry",
|
||||
[BPF_TRACE_FEXIT] = "fexit",
|
||||
[BPF_MODIFY_RETURN] = "mod_ret",
|
||||
[BPF_LSM_MAC] = "lsm_mac",
|
||||
};
|
||||
|
||||
extern const char * const map_type_name[];
|
||||
extern const size_t map_type_name_size;
|
||||
|
||||
@ -90,6 +124,7 @@ enum bpf_obj_type {
|
||||
BPF_OBJ_UNKNOWN,
|
||||
BPF_OBJ_PROG,
|
||||
BPF_OBJ_MAP,
|
||||
BPF_OBJ_LINK,
|
||||
};
|
||||
|
||||
extern const char *bin_name;
|
||||
@ -102,6 +137,7 @@ extern bool verifier_logs;
|
||||
extern bool relaxed_maps;
|
||||
extern struct pinned_obj_table prog_table;
|
||||
extern struct pinned_obj_table map_table;
|
||||
extern struct pinned_obj_table link_table;
|
||||
|
||||
void __printf(1, 2) p_err(const char *fmt, ...);
|
||||
void __printf(1, 2) p_info(const char *fmt, ...);
|
||||
@ -153,6 +189,7 @@ int do_pin_fd(int fd, const char *name);
|
||||
|
||||
int do_prog(int argc, char **arg);
|
||||
int do_map(int argc, char **arg);
|
||||
int do_link(int argc, char **arg);
|
||||
int do_event_pipe(int argc, char **argv);
|
||||
int do_cgroup(int argc, char **arg);
|
||||
int do_perf(int argc, char **arg);
|
||||
|
@ -113,6 +113,8 @@ enum bpf_cmd {
|
||||
BPF_MAP_DELETE_BATCH,
|
||||
BPF_LINK_CREATE,
|
||||
BPF_LINK_UPDATE,
|
||||
BPF_LINK_GET_FD_BY_ID,
|
||||
BPF_LINK_GET_NEXT_ID,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
@ -220,6 +222,15 @@ enum bpf_attach_type {
|
||||
|
||||
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
|
||||
|
||||
enum bpf_link_type {
|
||||
BPF_LINK_TYPE_UNSPEC = 0,
|
||||
BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
|
||||
BPF_LINK_TYPE_TRACING = 2,
|
||||
BPF_LINK_TYPE_CGROUP = 3,
|
||||
|
||||
MAX_BPF_LINK_TYPE,
|
||||
};
|
||||
|
||||
/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
|
||||
*
|
||||
* NONE(default): No further bpf programs allowed in the subtree.
|
||||
@ -523,6 +534,7 @@ union bpf_attr {
|
||||
__u32 prog_id;
|
||||
__u32 map_id;
|
||||
__u32 btf_id;
|
||||
__u32 link_id;
|
||||
};
|
||||
__u32 next_id;
|
||||
__u32 open_flags;
|
||||
@ -3609,6 +3621,25 @@ struct bpf_btf_info {
|
||||
__u32 id;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_link_info {
|
||||
__u32 type;
|
||||
__u32 id;
|
||||
__u32 prog_id;
|
||||
union {
|
||||
struct {
|
||||
__aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
|
||||
__u32 tp_name_len; /* in/out: tp_name buffer len */
|
||||
} raw_tracepoint;
|
||||
struct {
|
||||
__u32 attach_type;
|
||||
} tracing;
|
||||
struct {
|
||||
__u64 cgroup_id;
|
||||
__u32 attach_type;
|
||||
} cgroup;
|
||||
};
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
|
||||
* by user and intended to be used by socket (e.g. to bind to, depends on
|
||||
* attach attach type).
|
||||
|
@ -721,6 +721,11 @@ int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
return bpf_obj_get_next_id(start_id, next_id, BPF_BTF_GET_NEXT_ID);
|
||||
}
|
||||
|
||||
int bpf_link_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
{
|
||||
return bpf_obj_get_next_id(start_id, next_id, BPF_LINK_GET_NEXT_ID);
|
||||
}
|
||||
|
||||
int bpf_prog_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
@ -751,13 +756,23 @@ int bpf_btf_get_fd_by_id(__u32 id)
|
||||
return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
|
||||
int bpf_link_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.link_id = id;
|
||||
|
||||
return sys_bpf(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int err;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.info.bpf_fd = prog_fd;
|
||||
attr.info.bpf_fd = bpf_fd;
|
||||
attr.info.info_len = *info_len;
|
||||
attr.info.info = ptr_to_u64(info);
|
||||
|
||||
|
@ -216,10 +216,12 @@ LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
|
||||
LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
LIBBPF_API int bpf_link_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
|
||||
LIBBPF_API int bpf_map_get_fd_by_id(__u32 id);
|
||||
LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id);
|
||||
LIBBPF_API int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
|
||||
LIBBPF_API int bpf_link_get_fd_by_id(__u32 id);
|
||||
LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len);
|
||||
LIBBPF_API int bpf_prog_query(int target_fd, enum bpf_attach_type type,
|
||||
__u32 query_flags, __u32 *attach_flags,
|
||||
__u32 *prog_ids, __u32 *prog_cnt);
|
||||
|
@ -254,3 +254,9 @@ LIBBPF_0.0.8 {
|
||||
bpf_program__set_lsm;
|
||||
bpf_set_link_xdp_fd_opts;
|
||||
} LIBBPF_0.0.7;
|
||||
|
||||
LIBBPF_0.0.9 {
|
||||
global:
|
||||
bpf_link_get_fd_by_id;
|
||||
bpf_link_get_next_id;
|
||||
} LIBBPF_0.0.8;
|
||||
|
@ -1,26 +1,30 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
#define nr_iters 2
|
||||
|
||||
void test_bpf_obj_id(void)
|
||||
{
|
||||
const __u64 array_magic_value = 0xfaceb00c;
|
||||
const __u32 array_key = 0;
|
||||
const int nr_iters = 2;
|
||||
const char *file = "./test_obj_id.o";
|
||||
const char *expected_prog_name = "test_obj_id";
|
||||
const char *expected_map_name = "test_map_id";
|
||||
const __u64 nsec_per_sec = 1000000000;
|
||||
|
||||
struct bpf_object *objs[nr_iters];
|
||||
struct bpf_object *objs[nr_iters] = {};
|
||||
struct bpf_link *links[nr_iters] = {};
|
||||
struct bpf_program *prog;
|
||||
int prog_fds[nr_iters], map_fds[nr_iters];
|
||||
/* +1 to test for the info_len returned by kernel */
|
||||
struct bpf_prog_info prog_infos[nr_iters + 1];
|
||||
struct bpf_map_info map_infos[nr_iters + 1];
|
||||
struct bpf_link_info link_infos[nr_iters + 1];
|
||||
/* Each prog only uses one map. +1 to test nr_map_ids
|
||||
* returned by kernel.
|
||||
*/
|
||||
__u32 map_ids[nr_iters + 1];
|
||||
char jited_insns[128], xlated_insns[128], zeros[128];
|
||||
char jited_insns[128], xlated_insns[128], zeros[128], tp_name[128];
|
||||
__u32 i, next_id, info_len, nr_id_found, duration = 0;
|
||||
struct timespec real_time_ts, boot_time_ts;
|
||||
int err = 0;
|
||||
@ -36,14 +40,15 @@ void test_bpf_obj_id(void)
|
||||
CHECK(err >= 0 || errno != ENOENT,
|
||||
"get-fd-by-notexist-map-id", "err %d errno %d\n", err, errno);
|
||||
|
||||
for (i = 0; i < nr_iters; i++)
|
||||
objs[i] = NULL;
|
||||
err = bpf_link_get_fd_by_id(0);
|
||||
CHECK(err >= 0 || errno != ENOENT,
|
||||
"get-fd-by-notexist-link-id", "err %d errno %d\n", err, errno);
|
||||
|
||||
/* Check bpf_obj_get_info_by_fd() */
|
||||
bzero(zeros, sizeof(zeros));
|
||||
for (i = 0; i < nr_iters; i++) {
|
||||
now = time(NULL);
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT,
|
||||
&objs[i], &prog_fds[i]);
|
||||
/* test_obj_id.o is a dumb prog. It should never fail
|
||||
* to load.
|
||||
@ -60,6 +65,17 @@ void test_bpf_obj_id(void)
|
||||
if (CHECK_FAIL(err))
|
||||
goto done;
|
||||
|
||||
prog = bpf_object__find_program_by_title(objs[i],
|
||||
"raw_tp/sys_enter");
|
||||
if (CHECK_FAIL(!prog))
|
||||
goto done;
|
||||
links[i] = bpf_program__attach(prog);
|
||||
err = libbpf_get_error(links[i]);
|
||||
if (CHECK(err, "prog_attach", "prog #%d, err %d\n", i, err)) {
|
||||
links[i] = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check getting map info */
|
||||
info_len = sizeof(struct bpf_map_info) * 2;
|
||||
bzero(&map_infos[i], info_len);
|
||||
@ -107,7 +123,7 @@ void test_bpf_obj_id(void)
|
||||
load_time = (real_time_ts.tv_sec - boot_time_ts.tv_sec)
|
||||
+ (prog_infos[i].load_time / nsec_per_sec);
|
||||
if (CHECK(err ||
|
||||
prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||
|
||||
prog_infos[i].type != BPF_PROG_TYPE_RAW_TRACEPOINT ||
|
||||
info_len != sizeof(struct bpf_prog_info) ||
|
||||
(env.jit_enabled && !prog_infos[i].jited_prog_len) ||
|
||||
(env.jit_enabled &&
|
||||
@ -120,7 +136,11 @@ void test_bpf_obj_id(void)
|
||||
*(int *)(long)prog_infos[i].map_ids != map_infos[i].id ||
|
||||
strcmp((char *)prog_infos[i].name, expected_prog_name),
|
||||
"get-prog-info(fd)",
|
||||
"err %d errno %d i %d type %d(%d) info_len %u(%zu) jit_enabled %d jited_prog_len %u xlated_prog_len %u jited_prog %d xlated_prog %d load_time %lu(%lu) uid %u(%u) nr_map_ids %u(%u) map_id %u(%u) name %s(%s)\n",
|
||||
"err %d errno %d i %d type %d(%d) info_len %u(%zu) "
|
||||
"jit_enabled %d jited_prog_len %u xlated_prog_len %u "
|
||||
"jited_prog %d xlated_prog %d load_time %lu(%lu) "
|
||||
"uid %u(%u) nr_map_ids %u(%u) map_id %u(%u) "
|
||||
"name %s(%s)\n",
|
||||
err, errno, i,
|
||||
prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
info_len, sizeof(struct bpf_prog_info),
|
||||
@ -135,6 +155,33 @@ void test_bpf_obj_id(void)
|
||||
*(int *)(long)prog_infos[i].map_ids, map_infos[i].id,
|
||||
prog_infos[i].name, expected_prog_name))
|
||||
goto done;
|
||||
|
||||
/* Check getting link info */
|
||||
info_len = sizeof(struct bpf_link_info) * 2;
|
||||
bzero(&link_infos[i], info_len);
|
||||
link_infos[i].raw_tracepoint.tp_name = (__u64)&tp_name;
|
||||
link_infos[i].raw_tracepoint.tp_name_len = sizeof(tp_name);
|
||||
err = bpf_obj_get_info_by_fd(bpf_link__fd(links[i]),
|
||||
&link_infos[i], &info_len);
|
||||
if (CHECK(err ||
|
||||
link_infos[i].type != BPF_LINK_TYPE_RAW_TRACEPOINT ||
|
||||
link_infos[i].prog_id != prog_infos[i].id ||
|
||||
link_infos[i].raw_tracepoint.tp_name != (__u64)&tp_name ||
|
||||
strcmp((char *)link_infos[i].raw_tracepoint.tp_name,
|
||||
"sys_enter") ||
|
||||
info_len != sizeof(struct bpf_link_info),
|
||||
"get-link-info(fd)",
|
||||
"err %d errno %d info_len %u(%zu) type %d(%d) id %d "
|
||||
"prog_id %d (%d) tp_name %s(%s)\n",
|
||||
err, errno,
|
||||
info_len, sizeof(struct bpf_link_info),
|
||||
link_infos[i].type, BPF_LINK_TYPE_RAW_TRACEPOINT,
|
||||
link_infos[i].id,
|
||||
link_infos[i].prog_id, prog_infos[i].id,
|
||||
(char *)link_infos[i].raw_tracepoint.tp_name,
|
||||
"sys_enter"))
|
||||
goto done;
|
||||
|
||||
}
|
||||
|
||||
/* Check bpf_prog_get_next_id() */
|
||||
@ -247,7 +294,52 @@ void test_bpf_obj_id(void)
|
||||
"nr_id_found %u(%u)\n",
|
||||
nr_id_found, nr_iters);
|
||||
|
||||
/* Check bpf_link_get_next_id() */
|
||||
nr_id_found = 0;
|
||||
next_id = 0;
|
||||
while (!bpf_link_get_next_id(next_id, &next_id)) {
|
||||
struct bpf_link_info link_info;
|
||||
int link_fd, cmp_res;
|
||||
|
||||
info_len = sizeof(link_info);
|
||||
memset(&link_info, 0, info_len);
|
||||
|
||||
link_fd = bpf_link_get_fd_by_id(next_id);
|
||||
if (link_fd < 0 && errno == ENOENT)
|
||||
/* The bpf_link is in the dead row */
|
||||
continue;
|
||||
if (CHECK(link_fd < 0, "get-link-fd(next_id)",
|
||||
"link_fd %d next_id %u errno %d\n",
|
||||
link_fd, next_id, errno))
|
||||
break;
|
||||
|
||||
for (i = 0; i < nr_iters; i++)
|
||||
if (link_infos[i].id == next_id)
|
||||
break;
|
||||
|
||||
if (i == nr_iters)
|
||||
continue;
|
||||
|
||||
nr_id_found++;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(link_fd, &link_info, &info_len);
|
||||
cmp_res = memcmp(&link_info, &link_infos[i],
|
||||
offsetof(struct bpf_link_info, raw_tracepoint));
|
||||
CHECK(err || info_len != sizeof(link_info) || cmp_res,
|
||||
"check get-link-info(next_id->fd)",
|
||||
"err %d errno %d info_len %u(%zu) memcmp %d\n",
|
||||
err, errno, info_len, sizeof(struct bpf_link_info),
|
||||
cmp_res);
|
||||
|
||||
close(link_fd);
|
||||
}
|
||||
CHECK(nr_id_found != nr_iters,
|
||||
"check total link id found by get_next_id",
|
||||
"nr_id_found %u(%u)\n", nr_id_found, nr_iters);
|
||||
|
||||
done:
|
||||
for (i = 0; i < nr_iters; i++)
|
||||
for (i = 0; i < nr_iters; i++) {
|
||||
bpf_link__destroy(links[i]);
|
||||
bpf_object__close(objs[i]);
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,8 @@
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
/* It is a dumb bpf program such that it must have no
|
||||
* issue to be loaded since testing the verifier is
|
||||
* not the focus here.
|
||||
*/
|
||||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
@ -20,13 +12,13 @@ struct {
|
||||
__type(value, __u64);
|
||||
} test_map_id SEC(".maps");
|
||||
|
||||
SEC("test_obj_id_dummy")
|
||||
int test_obj_id(struct __sk_buff *skb)
|
||||
SEC("raw_tp/sys_enter")
|
||||
int test_obj_id(void *ctx)
|
||||
{
|
||||
__u32 key = 0;
|
||||
__u64 *value;
|
||||
|
||||
value = bpf_map_lookup_elem(&test_map_id, &key);
|
||||
|
||||
return TC_ACT_OK;
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user