首先会对prog_flags做一些校验,检查flags是否合法
后续是一些权限的判断,判断是否有权限使用bpf_prog_load这个函数
static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
{
enum bpf_prog_type type = attr->prog_type;
struct bpf_prog *prog, *dst_prog = NULL;
struct btf *attach_btf = NULL;
int err;
char license[128];
if (CHECK_ATTR(BPF_PROG_LOAD)) // helper macro to check that unused fields 'union bpf_attr' are zero
return -EINVAL;
if (attr->prog_flags & ~(BPF_F_STRICT_ALIGNMENT | // 严格对齐
BPF_F_ANY_ALIGNMENT | // 任意对齐
BPF_F_TEST_STATE_FREQ |
BPF_F_SLEEPABLE |
BPF_F_TEST_RND_HI32 |
BPF_F_XDP_HAS_FRAGS |
BPF_F_XDP_DEV_BOUND_ONLY)) // only these flags could be used
return -EINVAL;
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
(attr->prog_flags & BPF_F_ANY_ALIGNMENT) && // 这个任意对齐是需要对应CAP的
!bpf_capable()) // check privilege
return -EPERM;
/* Intent here is for unprivileged_bpf_disabled to block BPF program
* creation for unprivileged users; other actions depend
* on fd availability and access to bpffs, so are dependent on
* object creation success. Even with unprivileged BPF disabled,
* capability checks are still carried out for these
* and other operations.
*/
if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) // check privilege 前面的sysctl_unprivileged_bpf_disabled就是开关
return -EPERM;
这里的sysctl_unprivileged_bpf_disabled是之前所说的开关,决定普通权限用户是否能够载入bpf程序
后续还有对指令数量的check,普通用户最多支持4096指令数的bpf程序,特权用户支持1000000条
if (attr->insn_cnt == 0 ||
attr->insn_cnt > (bpf_capable() ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS))
return -E2BIG; // if bpf_capable -> 1000000, else -> 4096
然后是对bpf程序类型的check,普通用户只能使用BPF_PROG_TYPE_SOCKET_FILTER
或BPF_PROG_TYPE_CGROUP_SKB
这两种bpf程序类型
if (type != BPF_PROG_TYPE_SOCKET_FILTER &&
type != BPF_PROG_TYPE_CGROUP_SKB &&
!bpf_capable()) // is unprivileged, could only using these two type
return -EPERM;
对一些特殊类型的bpf程序还会做更多的权限校验
可以看到除了BPF_PROG_TYPE_CGROUP_SKB
和BPF_PROG_TYPE_SK_REUSEPORT
以外都需要CAP_NET_ADMIN
或者CAP_SYS_ADMIN
而如果是perfmon
类型的prog,则需要CAP_PERFMON
或CAP_SYS_ADMIN
这里不同的bpf程序类型可以调用的内核接口/权限等不一样
if (is_net_admin_prog_type(type) && !capable(CAP_NET_ADMIN) && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (is_perfmon_prog_type(type) && !perfmon_capable())
return -EPERM;
static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
{
switch (prog_type) {
case BPF_PROG_TYPE_SCHED_CLS:
case BPF_PROG_TYPE_SCHED_ACT:
case BPF_PROG_TYPE_XDP:
case BPF_PROG_TYPE_LWT_IN:
case BPF_PROG_TYPE_LWT_OUT:
case BPF_PROG_TYPE_LWT_XMIT:
case BPF_PROG_TYPE_LWT_SEG6LOCAL:
case BPF_PROG_TYPE_SK_SKB:
case BPF_PROG_TYPE_SK_MSG:
case BPF_PROG_TYPE_FLOW_DISSECTOR:
case BPF_PROG_TYPE_CGROUP_DEVICE:
case BPF_PROG_TYPE_CGROUP_SOCK:
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
case BPF_PROG_TYPE_SOCK_OPS:
case BPF_PROG_TYPE_EXT: /* extends any prog */
case BPF_PROG_TYPE_NETFILTER:
return true;
case BPF_PROG_TYPE_CGROUP_SKB:
/* always unpriv */
case BPF_PROG_TYPE_SK_REUSEPORT:
/* equivalent to SOCKET_FILTER. need CAP_BPF only */
default:
return false;
}
}
static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
{
switch (prog_type) {
case BPF_PROG_TYPE_KPROBE:
case BPF_PROG_TYPE_TRACEPOINT:
case BPF_PROG_TYPE_PERF_EVENT:
case BPF_PROG_TYPE_RAW_TRACEPOINT:
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
case BPF_PROG_TYPE_TRACING:
case BPF_PROG_TYPE_LSM:
case BPF_PROG_TYPE_STRUCT_OPS: /* has access to struct sock */
case BPF_PROG_TYPE_EXT: /* extends any prog */
return true;
default:
return false;
}
}
随后会去检查attr中的attach_prog_id
字段,这个字段指定了要附加的内核已有BPF程序id或内核的BTF信息,这主要关系到BPF的两个功能
附加内核已有的BPF程序为了实现程序链式调用(tail call机制),或者让一个程序在另一个程序的基础上扩展额外的处理逻辑。这样可以在不修改原程序的前提下添加新的功能,或者实现对现有行为的拦截和增强
利用BTF可以对类型进行安全检查,以及获取CO-RE支持。附加内核 BTF 信息使 BPF 程序能够获取内核数据结构的元数据,从而在加载时或运行时验证数据访问是否安全。这对 CO-RE(Compile Once – Run Everywhere,编译一次,到处运行)非常重要,因为程序可以利用内核提供的类型信息适应不同内核版本的变化,从而减少因结构偏移不同而产生的错误
当然这些都需要内核BTF支持
/* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog
* or btf, we need to check which one it is
*/
if (attr->attach_prog_fd) { // if attach prog fd
dst_prog = bpf_prog_get(attr->attach_prog_fd);
if (IS_ERR(dst_prog)) {
dst_prog = NULL;
attach_btf = btf_get_by_fd(attr->attach_btf_obj_fd);
if (IS_ERR(attach_btf))
return -EINVAL;
if (!btf_is_kernel(attach_btf)) {
/* attaching through specifying bpf_prog's BTF
* objects directly might be supported eventually
*/
btf_put(attach_btf);
return -ENOTSUPP;
}
}
} else if (attr->attach_btf_id) { // if attach btf id
/* fall back to vmlinux BTF, if BTF type ID is specified */
attach_btf = bpf_get_btf_vmlinux();
if (IS_ERR(attach_btf))
return PTR_ERR(attach_btf);
if (!attach_btf)
return -EINVAL;
btf_get(attach_btf);
}