xdp 是如何加载到内核并运行的

XDP framework

xdp在内核里的有三个关键步骤:

  • load: 加载到内核
  • attach: 绑定到一个网口
  • run:网口收包时候,调用并执行bpf prog

load加载: 通过ebpf系统调用, 把prog加载到内核

fd = sys_bpf(BPF_PROG_LOAD, attr, size);

  • 在内核里创建一个bfp_prog结构体用以存储bpf prog。
  • 通过bpf_check检查prog程序的安全性和合法性。
  • 通过bpf_prog_select_runtime指定bpf prog对应的执行函数
  • 这个函数指针保存在bpf_func这个字段里。这里的function最终指向通用的bfp run函数___bpf_prog_run
    关于___bpf_prog_run这个具体封装和实现见另外一篇文章。

attach绑定: 将prog程序绑定到一个特定的网口的struct net_device

libpf函数do_attach将上一步加载在内核里的prog跟一个网口绑定, 具体实现是通过下发netlink命令。
这是个generic类型的netlink命令,最终通过dev_change_xdp_fd将prog挂载到对应netdev下面。

1
2
3
4
5
6
2056 struct net_device {
2057 char name[IFNAMSIZ];
2058 struct netdev_name_node *name_node;
2059 struct dev_ifalias __rcu *ifalias;
...
2234 struct bpf_prog __rcu *xdp_prog;

struct net_device下的xdp_prog指向对应的prog

注意这里的加载分skb模式和DRV驱动模式。 驱动模式是加载到网卡驱动里面。以mlx5为例是加载到接收队列rq下面的xdp_prog

run运行: 当有网络报文从网口进入到协议栈处理时候,调用prog程序并运行

还是以SKB模式为例
网络协议栈在入口函数__netif_receive_skb时候,通过多层调用,最终会

1
2
3
4
5
6
--> __netif_receive_skb
--> --> __netif_receive_skb_one_core
--> --> --> netif_receive_generic_xdp
--> --> --> --> bpf_prog_run_generic_xdp
--> --> --> --> --> bpf_prog_run_xdp
--> --> --> --> --> --> __bpf_prog_run(prog, xdp, BPF_DISPATCHER_FUNC(xdp));

这个调用关系中的函数调用和宏定义的分析见

核心数据结构

bfp_prog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1461 struct bpf_prog {
1462 u16 pages; /* Number of allocated pages */
1463 u16 jited:1, /* Is our filter JIT'ed? */
1464 jit_requested:1,/* archs need to JIT the prog */
1465 gpl_compatible:1, /* Is filter GPL compatible? */
1466 cb_access:1, /* Is control block accessed? */
1467 dst_needed:1, /* Do we need dst entry? */
1468 blinding_requested:1, /* needs constant blinding */
1469 blinded:1, /* Was blinded */
1470 is_func:1, /* program is a bpf function */
1471 kprobe_override:1, /* Do we override a kprobe? */
1472 has_callchain_buf:1, /* callchain buffer allocated? */
1473 enforce_expected_attach_type:1, /* Enforce expected_attach_type checking at attach time */
1474 call_get_stack:1, /* Do we call bpf_get_stack() or bpf_get_stackid() */
1475 call_get_func_ip:1, /* Do we call get_func_ip() */
1476 tstamp_type_access:1; /* Accessed __sk_buff->tstamp_type */
1477 enum bpf_prog_type type; /* Type of BPF program */
1478 enum bpf_attach_type expected_attach_type; /* For some prog types */
1479 u32 len; /* Number of filter blocks */
1480 u32 jited_len; /* Size of jited insns in bytes */
1481 u8 tag[BPF_TAG_SIZE];
1482 struct bpf_prog_stats __percpu *stats;
1483 int __percpu *active;
1484 unsigned int (*bpf_func)(const void *ctx,
1485 const struct bpf_insn *insn);
1486 struct bpf_prog_aux *aux; /* Auxiliary fields */
1487 struct sock_fprog_kern *orig_prog; /* Original BPF program */
1488 /* Instructions for interpreter */
1489 union {
1490 DECLARE_FLEX_ARRAY(struct sock_filter, insns);
1491 DECLARE_FLEX_ARRAY(struct bpf_insn, insnsi);
1492 };
1493 };

函数关系汇总图

case 1