XFRM 设备 - 卸载 IPsec 计算¶
Shannon Nelson <shannon.nelson@oracle.com> Leon Romanovsky <leonro@nvidia.com>
概述¶
IPsec 是保护网络流量的有用功能,但计算成本很高:一个 10Gbps 的链路很容易被降到 1Gbps 以下,这取决于流量和链路配置。幸运的是,有一些网卡提供了基于硬件的 IPsec 卸载,可以大幅提高吞吐量并降低 CPU 利用率。XFRM 设备接口允许网卡驱动程序向堆栈提供对硬件卸载的访问权限。
- 目前,内核支持两种类型的硬件卸载。
IPsec 加密卸载:* 网卡执行加密/解密 * 内核执行其他所有操作
IPsec 数据包卸载:* 网卡执行加密/解密 * 网卡执行封装 * 内核和网卡保持 SA 和策略同步 * 网卡处理 SA 和策略状态 * 内核与密钥管理器通信
对卸载的用户态访问通常通过诸如 libreswan 或 KAME/raccoon 之类的系统进行,但在实验时,iproute2 的 'ip xfrm' 命令集可能很有用。对于加密卸载,一个示例命令可能如下所示:
- ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport
reqid 0x07 replay-window 32 aead ‘rfc4106(gcm(aes))’ 0x44434241343332312423222114131211f4f3f2f1 128 sel src 14.0.0.52/24 dst 14.0.0.70/24 proto tcp offload dev eth4 dir in
对于数据包卸载:
- ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport
reqid 0x07 replay-window 32 aead ‘rfc4106(gcm(aes))’ 0x44434241343332312423222114131211f4f3f2f1 128 sel src 14.0.0.52/24 dst 14.0.0.70/24 proto tcp offload packet dev eth4 dir in
ip x p add src 14.0.0.70 dst 14.0.0.52 offload packet dev eth4 dir in tmpl src 14.0.0.70 dst 14.0.0.52 proto esp reqid 10000 mode transport
是的,这很丑陋,但这正是 shell 脚本和/或 libreswan 的用途。
要实现的 回调¶
/* from include/linux/netdevice.h */
struct xfrmdev_ops {
/* Crypto and Packet offload callbacks */
int (*xdo_dev_state_add) (struct xfrm_state *x, struct netlink_ext_ack *extack);
void (*xdo_dev_state_delete) (struct xfrm_state *x);
void (*xdo_dev_state_free) (struct xfrm_state *x);
bool (*xdo_dev_offload_ok) (struct sk_buff *skb,
struct xfrm_state *x);
void (*xdo_dev_state_advance_esn) (struct xfrm_state *x);
void (*xdo_dev_state_update_stats) (struct xfrm_state *x);
/* Solely packet offload callbacks */
int (*xdo_dev_policy_add) (struct xfrm_policy *x, struct netlink_ext_ack *extack);
void (*xdo_dev_policy_delete) (struct xfrm_policy *x);
void (*xdo_dev_policy_free) (struct xfrm_policy *x);
};
提供 IPsec 卸载的网卡驱动程序需要实现与受支持的卸载相关的回调,以使网络堆栈的 XFRM 子系统可以使用该卸载。此外,功能位 NETIF_F_HW_ESP 和 NETIF_F_HW_ESP_TX_CSUM 将指示卸载的可用性。
流程¶
在探测时以及调用 register_netdev()
之前,驱动程序应设置本地数据结构和 XFRM 回调,并设置功能位。XFRM 代码的侦听器将在 NETDEV_REGISTER 上完成设置。
adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops;
adapter->netdev->features |= NETIF_F_HW_ESP;
adapter->netdev->hw_enc_features |= NETIF_F_HW_ESP;
当使用 “offload” 功能的请求设置新的 SA 时,驱动程序的 xdo_dev_state_add() 将获得要卸载的新 SA,并指示它是用于 Rx 还是 Tx。驱动程序应:
验证该算法是否支持卸载
存储 SA 信息(密钥、盐值、目标 IP、协议等)
启用 SA 的硬件卸载
返回状态值
0
成功
-EOPNETSUPP
不支持卸载,请尝试 SW IPsec,不适用于数据包卸载模式
其他
使请求失败
驱动程序还可以在 SA 中设置一个 offload_handle,这是一个不透明的 void 指针,可用于将上下文传递到快速路径卸载请求中
xs->xso.offload_handle = context;
当网络堆栈准备一个已设置为卸载的 SA 的 IPsec 数据包时,它首先使用 skb 和预期的卸载状态调用 xdo_dev_offload_ok(),以询问驱动程序是否可以提供卸载服务。这可以检查数据包信息以确保可以支持卸载(例如,IPv4 或 IPv6,无 IPv4 选项等),并返回 true 或 false 以表示其支持。
加密卸载模式:准备发送时,驱动程序需要检查 Tx 数据包的卸载信息,包括不透明的上下文,并相应地设置数据包发送
xs = xfrm_input_state(skb);
context = xs->xso.offload_handle;
set up HW for send
堆栈已将适当的 IPsec 标头插入数据包数据中,卸载只需要执行加密并修复标头值。
当收到数据包且硬件已指示它卸载了解密时,驱动程序需要在数据包的 skb 中添加对解码的 SA 的引用。此时,数据应该已被解密,但 IPsec 标头仍在数据包数据中;它们稍后会在堆栈中的 xfrm_input() 中删除。
查找并保留用于 Rx skb 的 SA
get spi, protocol, and destination IP from packet headers xs = find xs from (spi, protocol, dest_IP) xfrm_state_hold(xs);将状态信息存储到 skb 中
sp = secpath_set(skb); if (!sp) return; sp->xvec[sp->len++] = xs; sp->olen++;指示卸载的成功和/或错误状态
xo = xfrm_offload(skb); xo->flags = CRYPTO_DONE; xo->status = crypto_status;像往常一样将数据包交给 napi_gro_receive()
在 ESN 模式下,从 xfrm_replay_advance_esn() 调用 xdo_dev_state_advance_esn()。如果需要,驱动程序将检查数据包序列号并更新 HW ESN 状态机。
数据包卸载模式:HW 添加和删除 XFRM 标头。因此,在 RX 路径中,如果 HW 报告成功,则会绕过 XFRM 堆栈。在 TX 路径中,数据包在没有额外标头且未加密的情况下离开内核,HW 负责执行此操作。
当用户删除 SA 时,会要求驱动程序的 xdo_dev_state_delete() 和 xdo_dev_policy_delete() 禁用卸载。稍后,在删除对状态和策略的所有引用计数后,并且可以为卸载状态清除任何剩余资源之后,从垃圾回收例程调用 xdo_dev_state_free() 和 xdo_dev_policy_free()。驱动程序如何使用这些将取决于特定的硬件需求。
当 netdev 设置为 DOWN 时,XFRM 堆栈的 netdev 侦听器将对任何剩余的卸载状态调用 xdo_dev_state_delete()、xdo_dev_policy_delete()、xdo_dev_state_free() 和 xdo_dev_policy_free()。
硬件处理数据包的结果,XFRM 核心无法计算硬限制和软限制。HW/驱动程序负责执行此操作,并在调用 xdo_dev_state_update_stats() 时提供准确的数据。如果发生其中一个限制,则驱动程序需要调用 xfrm_state_check_expire() 以确保 XFRM 执行重新密钥序列。