DevilKing's blog

冷灯看剑,剑上几分功名?炉香无需计苍生,纵一穿烟逝,万丈云埋,孤阳还照古陵

0%

原文链接

网络监控器,都是作为用户级进程运行的。为了分析只在内核空间运行的数据,它们必须将这些数据从内核空间复制到用户空间的内存中去,并进行上下文切换。这与直接在内核空间分析这些数据相比,导致了巨大的性能开销

img

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
2
3
4
5
6
7
struct bpf_map_def SEC("maps") my_bpf_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 100,
.map_flags = BPF_F_NO_PREALLOC,
};

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网络流程:

  1. 网络的构建始于根节点Root Node(黑色)
  2. 构建Alpha网络,根据rule_1获取执行条件(module模式)中参数类型,添加Type Node节点(Object Type Node),并将module作为AlphaNode加入网络(已添加忽略),重复执行所有rule下的module,构建Alpha内存表 (Alpha Memory 黄色节点)。
  3. 构建Beta网络,Beta Node节点,Beta(i)左输入节点为Beta(i-1),右输入节点为Alpha(i),连接节点Join Node(绿色节点)
  4. 规则执行部分封装成Terminal Node(灰色节点)

有向图结构(如有向无环图DAG),图的解析主要是进行深度或广度遍历,可以执行每条分支流程,因此图的遍历更适合做并行网关,而决策流是排它网关语义,同时有且只有一个分支满足执行条件,使用图结构并不合适,一次决策的执行流程,更像一个链式流程,更适合单向链表结构。

关于flink的原子粒度的问题,采用滑动窗口的问题

如果要看 10 分钟、15 分钟数据,只要把结果数据再做一次聚合即可。这样可以避免因调整聚合窗口时间,导致监控服务重启不可用。

原文链接

image-20210204112953710 image-20210204113757023

通过reward值,可以形成矩阵

image-20210204113837798

将agent的每一次探索称为一个episode,即从任意初始状态到达目标状态

image-20210204114509095

原文链接

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut x: i32 = 22;
let mut v: Vec<&i32> = vec![];
let r: &mut Vec<&i32> = &mut v;
let p: &i32 = &x; // 1. `x` is borrowed here to create `p`
r.push(p); // 2. `p` is stored into `v`, but through `r`
x += 1; // <-- Error! can't mutate `x` while borrowed
take(v); // 3. the reference to `x` is later used here
}

fn take<T>(p: T) { .. }

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

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// create a degree counting differential dataflow
let (mut input, probe) = worker.dataflow(|scope| {

// create edge input, count a few ways.
let (input, edges) = scope.new_collection();

let out_degr_distr =
edges.map(|(src, _dst)| src) // extract source
.count(); // count occurrences of source
.map(|(_src, deg)| deg) // extract degree
.count(); // count occurrences of degree

// show us something about the collection, notice when done.
let probe =
out_degr_distr
.inspect(|x| println!("observed: {:?}", x))
.probe();

(input, probe)
});

subsets relationships

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let mut x: i32 = 22;

let mut v: Vec<&'0 i32> = vec![];

let r: &'1 mut Vec<&'2 i32> = &'3 mut v;
// requires: &'3 mut Vec<&'0 i32> <: &'1 mut Vec<&'2 i32>
// => '3: '1, '0: '2, '2: '0

let p: &'5 i32 = &'4 x;
// requires: &'4 i32 <: &'5 i32
// => '4: '5

r.push(p);
// requires: &'5 i32 <: &'2 i32
// => '5: '2

x += 1;

take::<Vec<&'6 i32>>(v);
// requires: Vec<&'0 i32> <: Vec<&'6 i32>
// => '0: '6
}

base_subset概念

This input is defined for each borrow expression (e.g., &x or &mut v) in the program.