Linux 4.18开始支持bpfilter,用于代替netfilter,其工作原理见下图:
可以看到bpfilter需要一个UMH(User Module Helper)实现eBPF的规则转换,这个UMH是需要运行在用户空间的。在内核源码下的net/bpfilter
可以看到以下文件:
.
├── bpfilter_kern.c
├── bpfilter_umh_blob.S
├── Kconfig
├── main.c
├── Makefile
└── msgfmt.h
其中,main.c
就是要在用户空间运行的bpfilter_umh的源代码,main.c
将编译成net/bpfilter/bpfilter_umh
,然后在bpfilter_umh_blob.S
里面用incbin
将编译好的bpfilter_umh
包进去:
/* net/bpfilter/bpfilter_umh_blob.S */
/* SPDX-License-Identifier: GPL-2.0 */
.section .init.rodata, "a"
.global bpfilter_umh_start
bpfilter_umh_start:
.incbin "net/bpfilter/bpfilter_umh"
.global bpfilter_umh_end
bpfilter_umh_end:
bpfilter在加载的时候将bpfilter_umh fork到用户空间里运行:
/* net/bpfilter/bpfilter_kern.c */
static int __init load_umh(void)
{
int err;
/* fork usermode process */
err = fork_usermode_blob(&bpfilter_umh_start,
&bpfilter_umh_end - &bpfilter_umh_start,
&info);
if (err)
return err;
pr_info("Loaded bpfilter_umh pid %d\n", info.pid);
/* health check that usermode process started correctly */
if (__bpfilter_process_sockopt(NULL, 0, 0, 0, 0) != 0) {
stop_umh();
return -EFAULT;
}
if (IS_ENABLED(CONFIG_INET))
bpfilter_process_sockopt = &__bpfilter_process_sockopt;
return 0;
}
# net/bpfilter/Kconfig
menuconfig BPFILTER
bool "BPF based packet filtering framework (BPFILTER)"
depends on NET && BPF && INET
help
This builds experimental bpfilter framework that is aiming to
provide netfilter compatible functionality via BPF
if BPFILTER
config BPFILTER_UMH
tristate "bpfilter kernel module with user mode helper"
depends on $(success,$(srctree)/scripts/cc-can-link.sh $(CC))
default m
help
This builds bpfilter kernel module with embedded user mode helper
endif
要特别注意的是,在Kconfig中,如果CONFIG_BPFILTER_UMH
为m
,那么bpfilter_umh将不会使用静态链接的方式编译,这样一来,如果目标系统使用的libc与编译内核的工具链使用的libc差距过大的话,会导致bpfilter_umh无法工作,也就意味着bpfilter模块无法装载。
例如,使用glibc的工具链编译的启用bpfilter支持的内核,放在musl环境的OpenWrt中,由于C库不同致使bpfilter_umh无法运行,bpfilter模块也就不能加载,最终导致iptables报错:
root@OpenWrt:~# iptables -L
iptables v1.6.2: can't initialize iptables table `filter': No child process
为了解决这个问题,需要将CONFIG_BPFILTER_UMH
设定为y
,这样会给bpfilter_umh添加-static
的链接参数(静态链接),就不会产生libc的依赖问题了。