Ubuntu20.04下如何使用BPF过滤其他应用现有通信的以太网帧
解答
结论先说:完全可以,Ubuntu 20.04 自带的5.4版本原生内核就支持相关能力,不需要升级内核、不需要修改gstreamer源码,有两种成熟落地方案,都能实现在内核层丢包、不让冗余RTP帧送到用户态gstreamer进程的效果,完全匹配你降低解码负载的需求。
方案一:tc 挂载 BPF 到网卡入站路径(推荐,零侵入)
这个方案不需要对gstreamer进程做任何注入操作,过滤点位于网卡收到数据包之后、进入内核协议栈之前,丢包开销最低,配置最简单。
- 第一步:安装依赖组件
apt update && apt install -y iproute2 linux-tools-common linux-tools-$(uname -r) clang llvm
- 第二步:编写BPF过滤程序
核心逻辑为:解析入站数据包的以太网头→IP头→UDP头→RTP头,匹配目标端口为你配置的各路摄像头收流端口(比如示例里的5550,以及其他几路对应的端口),按照你离线测试验证过的RTP序号间隔规则执行丢包,其余数据包直接放行。因为你的摄像头输出全是I帧,不需要做帧类型解析、不需要考虑帧间参考,等间隔丢包不会出现花屏,BPF逻辑可以写得非常轻量。 - 第三步:挂载BPF规则
编译BPF程序为目标文件后,先给你连接摄像头对应的物理网卡添加clsact队列规则(以下示例里网卡名为enp0s3,替换成你实际的网卡名即可):
tc qdisc add dev enp0s3 clsact
再将编译好的BPF程序挂载到网卡入站方向:
tc filter add dev enp0s3 ingress bpf obj rtp_drop.o sec classifier direct-action
这个方案的额外优势是可以通过BPF map动态调整丢包间隔,不需要重新加载程序,比如你需要临时调整保留帧率时,直接向map写入新的参数即可,全程不中断视频流。
方案二:BPF套接字过滤器注入到gstreamer现有套接字(精确匹配进程)
如果你需要保证过滤规则只作用于gstreamer进程的流量,完全不影响同端口的其他潜在流量,可以选择这个方案,过滤点位于UDP层将数据包放入套接字接收队列之前,同样不会把待丢弃的包送到用户态。
操作流程:
- 第一步:定位目标套接字
先正常启动gstreamer拉流,通过以下命令找到gstreamer进程的PID,以及对应收流端口的套接字fd:
# 找到gstreamer进程PID pidof gst-launch-1.0 # 找到对应端口的套接字inode,再从/proc/<PID>/fd目录下匹配到对应的fd号 ss -ulnp | grep gst-launch-1.0
- 第二步:挂载过滤器
编写简单的BPF加载器,通过pidfd_open获取目标进程的套接字fd句柄,调用setsockopt传入SO_ATTACH_BPF参数,将你写好的RTP丢包BPF程序挂载到对应套接字上即可。
这个方案的性能和方案一的差异对你5路30FPS的视频流场景完全可以忽略,只是配置步骤稍多。
注意事项
注意:传统libpcap使用的BPF机制只能获取数据包副本、无法干预原数据包的投递路径,不要用libpcap实现这个需求,达不到内核丢包降低负载的效果。
- 规则生效后可以通过
bpftool prog show查看BPF程序的处理、丢包计数,也可以通过top观察gstreamer的CPU占用,和你离线重放测试的效果一致。 - 如果要清除tc上挂载的BPF规则,执行
tc qdisc del dev enp0s3 clsact即可恢复正常流量。
内容的提问来源于stack exchange,提问作者Tik0
相关产品推荐
相关产品推荐

