简要说明:
- 当
CONFIG_XFRM=n 且 BR_NETFILTER=n 时,objdiff 显示无变化
- 设置一个或两个选项时,内核尺寸略有减小
- ipsec 性能无变化
自 v1 版本以来的变更:
- 从 kmem_cache 分配整个扩展空间。
- 在 refcnt == 1 的情况下,避免在
skb_ext_put() 中使用 atomic_dec_and_test 操作。 (类似于 kfree_skbmem() 中的 fclone_ref 用法)。
此补丁系列为 Linux 网络子系统引入了一个可选的扩展基础设施,其首批使用者是 IPsec (xfrm) 和网桥 netfilter。
第三个(未来)用户是多路径 TCP (MPTCP),它目前还未集成到内核主线中。 MPTCP 需要将逻辑 mptcp 序列号映射到各个子流所使用的 TCP 序列号。
DSS 映射在接收数据包时从 TCP 选项空间读取/写入,并在发送属于 MPTCP 连接的 TCP 数据包时写入到 TCP 选项空间。
如果尝试扩展 skb_shared_info 或向 skb 的 fclone 区域添加私有数据字段,这对于接收到的数据包 skb 是无效的,因此接收端需要一种不同的 DSS 信息传递方法。
MPTCP 与 secpath/网桥 netfilter 具有相同的需求:
- 扩展内存在 sk_buff 释放时被释放。
- 克隆 skb 后数据共享(克隆体继承扩展)。
- 向 skb 添加扩展时,会在需要时对扩展缓冲区执行写时复制 (COW)。
sk_buff 结构体添加了两个新成员:
-
active_extensions 字节(填补了一个空洞),用于指示此 skb 有哪些扩展可用。 这有两个目的:
a) 避免初始化指针的需要。
b) 允许通过清除 ->active_extensions 中的对应位来“删除”扩展。
虽然可以将 active_extensions 字节存储在扩展结构体本身而不是 sk_buff 中,但这存在一个问题:当我们需要禁用某个扩展时,如果信息存储在扩展缓冲区自身,处理一个已克隆的 skb 时可能需要先对其进行 COW 操作。如果此时内存分配 (kmalloc) 失败,我们将无法关闭该扩展。而直接清除 skb->active_extensions 中的位则总是可行的。
-
扩展指针,位于 sk_buff 结构体的末尾。 如果 active_extensions 字节为 0,则指针未定义,在 skb 分配时不进行初始化。
这为 skb 克隆和释放路径增加了额外的代码(用于处理扩展区域的引用计数/释放),但在后续补丁中,它将替换那些管理 skb->nf_bridge 和 skb->sp 结构体的类似代码。
可以为那些不支持在克隆时保留的扩展添加相应支持:
- 定义一个位掩码,包含所有需要在克隆时被复制/COW 的扩展。
- 修改
__skb_ext_copy() 以检查 ->active_extensions & SKB_EXT_PRESERVE_ON_CLONE。
- 如果测试结果为假,则将
clone->active_extensions 设置为 0。
此处未实现该功能,因为在此补丁系列中添加的所有扩展都需要复制/COW 语义。
最后一个补丁转换了 skb->sp,将 secpath 信息存储为新的 SKB_EXT_SEC_PATH 扩展,从而将 sp 指针从 skbuff 结构体中移除。
添加到 skb 克隆和释放路径的额外代码(用于处理扩展区域的引用计数/释放)实际上替换了现有的对 skb->nf_bridge 和 skb->secpath 执行相同操作的代码,因此可以视为一种代码重构和统一。
我没有看到内核树中的其他用户会从此基础设施中立即受益。仅为存储单个标志位(如 skb->nf_trace)而添加一个扩展是没有意义的。
那么,何时为 sk_buff 添加新扩展是一个好的选择呢?通常当满足以下所有条件时:
- 数据与 skb/数据包的生命周期紧密相关。
- 数据应在 skb 释放时被一同释放。
- 在正常网络处理流程(如 UDP、TCP 转发等常见工作负载)中,该数据通常不相关或不需要。
- 在克隆/释放时不需要执行复杂的操作,例如回调到特定内核模块。
Florian Westphal (13):
netfilter: avoid using skb->nf_bridge directly
sk_buff: add skb extension infrastructure
net: convert bridge_nf to use skb extension infrastructure
xfrm: change secpath_set to return secpath struct, not error value
net: move secpath_exist helper to sk_buff.h
net: use skb_sec_path helper in more places
drivers: net: intel: use secpath helpers in more places
drivers: net: ethernet: mellanox: use skb_sec_path helper
drivers: net: netdevsim: use skb_sec_path helper
xfrm: use secpath_exist where applicable
drivers: chelsio: use skb_sec_path helper
xfrm: prefer secpath_set over secpath_dup
net: switch secpath to use skb extension infrastructure
Documentation/networking/xfrm_device.txt | 7
drivers/crypto/chelsio/chcr_ipsec.c | 4
drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c | 15
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 5
drivers/net/ethernet/intel/ixgbevf/ipsec.c | 15
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 2
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c | 19
drivers/net/netdevsim/ipsec.c | 7
include/linux/netfilter_bridge.h | 33 +
include/linux/skbuff.h | 152 ++++++-
include/net/netfilter/br_netfilter.h | 14
include/net/xfrm.h | 41 --
net/Kconfig | 4
net/bridge/br_netfilter_hooks.c | 39 -
net/bridge/br_netfilter_ipv6.c | 4
net/core/skbuff.c | 201 +++++++++-
net/ipv4/esp4.c | 9
net/ipv4/esp4_offload.c | 15
net/ipv4/ip_output.c | 1
net/ipv4/netfilter/nf_reject_ipv4.c | 6
net/ipv6/esp6.c | 9
net/ipv6/esp6_offload.c | 15
net/ipv6/ip6_output.c | 1
net/ipv6/netfilter/nf_reject_ipv6.c | 10
net/ipv6/xfrm6_input.c | 8
net/netfilter/nf_log_common.c | 20
net/netfilter/nf_queue.c | 50 +-
net/netfilter/nfnetlink_queue.c | 23 -
net/netfilter/nft_meta.c | 2
net/netfilter/nft_xfrm.c | 2
net/netfilter/xt_physdev.c | 2
net/netfilter/xt_policy.c | 2
net/xfrm/Kconfig | 1
net/xfrm/xfrm_device.c | 4
net/xfrm/xfrm_input.c | 76 +--
net/xfrm/xfrm_interface.c | 2
net/xfrm/xfrm_output.c | 7
net/xfrm/xfrm_policy.c | 19
security/selinux/xfrm.c | 4
39 files changed, 564 insertions(+), 286 deletions(-)
原文链接
https://lwn.net/Articles/775255/
本文深入分析了 Linux 内核 网络协议栈的关键优化,更多关于网络底层与系统编程的讨论,欢迎访问云栈社区。