BPF Intro
网络监控器,都是作为用户级进程运行的。为了分析只在内核空间运行的数据,它们必须将这些数据从内核空间复制到用户空间的内存中去,并进行上下文切换。这与直接在内核空间分析这些数据相比,导致了巨大的性能开销
BPF 在数据包过滤上引入了两大革新:
- 一个新的虚拟机 (VM) 设计,可以有效地工作在基于寄存器结构的 CPU 之上;
- 应用程序使用缓存只复制与过滤数据包相关的数据,不会复制数据包的所有信息,最大程度地减少BPF 处理的数据,提高处理效率;
在目前的Linux内核中已经有了近10种的钩子,如下所示:
- kernel functions (kprobes)
- userspace functions (uprobes)
- system calls
- fentry/fexit
- Tracepoints
- network devices (tc/xdp)
- network routes
- TCP congestion algorithms
- sockets (data level)
从文件打开、创建TCP链接、Socket链接到发送系统消息等几乎所有的系统调用,加上用户空间的各种动态信息,都能加载BPF程序,可以说是无所不能。它们在内核中形成不同的BPF程序类型,在加载时会有类型判断。
BPF Map
1 | struct bpf_map_def SEC("maps") my_bpf_map = { |
BPF Helper
通过定义和维护BPF辅助函数,由BPF辅助函数来去面对后端的内核函数的变化,对开发者透明,形成稳定API接口。
- eBPF 程序中循环次数限制且必须在有限时间内结束
- eBPF 堆栈大小被限制在 MAXBPFSTACK,截止到内核 Linux 5.8 版本,被设置为 512。目前没有计划增加这个限制,解决方法是改用 BPF Map,它的大小是无限的。
- eBPF 字节码大小最初被限制为 4096 条指令,截止到内核 Linux 5.8 版本, 当前已将放宽至 100 万指令( BPF_COMPLEXITY_LIMIT_INSNS),对于无权限的BPF程序,仍然保留4096条限制 ( BPF_MAXINSNS )
Cilium
通过使用基于Linux内核特性的新技术——BPF,提供了基于service/pod/container作为标识,而非传统的IP地址,来定义和加强容器和Pod之间网络层、应用层的安全策略
Falco主要使用了raw_tracepoint的系统调用hook,检测应用进程的启动和退出,然后通过Perf类型的BPF Map将检测到的数据发送回用户空间,实现监控的pipeline。
风控系统
规则配置抽象化
决策树同决策矩阵部分?
首先确定规则集,然后再确定决策树
pipeline模型,用链式串联不同节点组件,组成直线工作流
Rete模型,构建成网络,区分alpha网络和beta网络
实现决策树和决策矩阵是把每个可选的条件都置为单独规则处理,而rule_1和rule_2存在互斥关系,如果一个为true,另一个则无需计算,rule_3和rule_4也是如此。如果决策树更加复杂,一个选择即一个分支,将存在更多无需计算的规则。为了提高决策效率,一个简单的实现方案:可将互斥的规则增加个属性标签“规则组”,将决策上下文缓存,已获得true结果的规则组下规则不再参与重复匹配计算。
实际上,是一个模式匹配的问题,如何更高效地匹配和轮转
rete模型
构建rete网络流程:
- 网络的构建始于根节点Root Node(黑色)
- 构建Alpha网络,根据rule_1获取执行条件(module模式)中参数类型,添加Type Node节点(Object Type Node),并将module作为AlphaNode加入网络(已添加忽略),重复执行所有rule下的module,构建Alpha内存表 (Alpha Memory 黄色节点)。
- 构建Beta网络,Beta Node节点,Beta(i)左输入节点为Beta(i-1),右输入节点为Alpha(i),连接节点Join Node(绿色节点)
- 规则执行部分封装成Terminal Node(灰色节点)
有向图结构(如有向无环图DAG),图的解析主要是进行深度或广度遍历,可以执行每条分支流程,因此图的遍历更适合做并行网关,而决策流是排它网关语义,同时有且只有一个分支满足执行条件,使用图结构并不合适,一次决策的执行流程,更像一个链式流程,更适合单向链表结构。
关于flink的原子粒度的问题,采用滑动窗口的问题
如果要看 10 分钟、15 分钟数据,只要把结果数据再做一次聚合即可。这样可以避免因调整聚合窗口时间,导致监控服务重启不可用。
Q-learning
borrow checker
1 | fn main() { |
if you have a mutable loan like r = &mut v
, then you can only access the value v
through the reference r
. Accessing v
directly in any way – read, write, or move – would invalidate the loan.
Datalog rules
Differential dataflow is a data-parallel programming framework designed to efficiently process large volumes of data and to quickly respond to arbitrary changes in input collections.
using familiar operators like map
, filter
, join
, and group
.
1 | // create a degree counting differential dataflow |
subsets relationships
1 | fn main() { |
base_subset概念
This input is defined for each borrow expression (e.g., &x
or &mut v
) in the program.