BPF sk_lookup 程序

BPF sk_lookup 程序类型 (BPF_PROG_TYPE_SK_LOOKUP) 将可编程性引入到传输层执行的套接字查找中,当数据包需要在本地交付时。

当调用 BPF sk_lookup 程序时,可以通过调用 bpf_sk_assign() BPF 辅助函数来选择将接收传入数据包的套接字。

TCP 和 UDP 都存在用于常见连接点 (BPF_SK_LOOKUP) 的钩子。

动机

引入 BPF sk_lookup 程序类型是为了解决在某些设置场景中,使用 bind() 套接字调用将套接字绑定到地址是不切实际的,例如

  1. 在一定 IP 地址范围内接收连接,例如 192.0.2.0/24,当由于端口冲突而无法绑定到通配符地址 INADRR_ANY 时,

  2. 接收所有或很大范围的端口上的连接,即 L7 代理用例。

这样的设置将需要创建并将一个套接字 bind() 到范围内的每个 IP 地址/端口,导致资源消耗和套接字查找期间潜在的延迟峰值。

附件

可以使用 bpf(BPF_LINK_CREATE, ...) 系统调用,使用 BPF_SK_LOOKUP 附加类型和 netns FD 作为附件 target_fd,将 BPF sk_lookup 程序附加到网络命名空间。

可以将多个程序附加到一个网络命名空间。 程序将按照附加的顺序调用。

钩子

每当传输层需要为传入数据包找到一个侦听(TCP)或未连接(UDP)套接字时,都会运行附加的 BPF sk_lookup 程序。

到已建立(TCP)和已连接(UDP)套接字的传入流量将像往常一样传送,而不会触发 BPF sk_lookup 钩子。

附加的 BPF 程序必须以 SK_PASSSK_DROP 判决代码返回。 对于其他作为网络过滤器的 BPF 程序类型,SK_PASS 表示套接字查找应继续到常规的基于哈希表的查找,而 SK_DROP 导致传输层丢弃数据包。

BPF sk_lookup 程序还可以通过调用 bpf_sk_assign() BPF 辅助函数来选择一个套接字来接收数据包。 通常,该程序在保存套接字的映射中查找套接字,例如 SOCKMAPSOCKHASH,并将 struct bpf_sock * 传递给 bpf_sk_assign() 辅助函数以记录选择。 仅当程序以 SK_PASS 代码终止时,选择套接字才会生效。

当附加多个程序时,最终结果由所有程序的返回代码根据以下规则确定

  1. 如果任何程序返回 SK_PASS 并选择了一个有效的套接字,则该套接字将用作套接字查找的结果。

  2. 如果多个程序返回 SK_PASS 并选择了一个套接字,则最后一个选择生效。

  3. 如果任何程序返回 SK_DROP,并且没有程序返回 SK_PASS 并选择了一个套接字,则套接字查找失败。

  4. 如果所有程序都返回 SK_PASS 并且没有程序选择套接字,则套接字查找继续。

API

在其上下文中,struct bpf_sk_lookup 的实例,BPF sk_lookup 程序接收有关触发套接字查找的数据包的信息。 即

  • IP 版本 (AF_INETAF_INET6),

  • L4 协议标识符 (IPPROTO_TCPIPPROTO_UDP),

  • 源和目标 IP 地址,

  • 源和目标 L4 端口,

  • 已使用 bpf_sk_assign() 选择的套接字。

有关详细信息,请参阅 linux/bpf.h 用户 API 标头中的 struct bpf_sk_lookup 声明,以及 bpf-helpers(7) 手册页中的 bpf_sk_assign() 部分。

示例

有关参考实现,请参阅 tools/testing/selftests/bpf/prog_tests/sk_lookup.c