XFRM–A Kernel Implementation Framework of IPsec Protocol
IPsec 协议帮助IP层建立安全可信的数据传输通道。目前有StrongSwan、OpenSwan等成熟的解决方案,它们都使用Linux内核 XFRM 框架接收和发送消息。
XFRM的正确发音是transform(转换)。它代表了内核协议栈接收IPsec消息时需要经过的转换过程,以恢复到原始消息;同样地,要发送的原始消息也需要转换为IPsec消息才能发送出去。
概述
XFRM 示例
IPsec有两个重要的概念:安全关联(Security Association)和安全策略(Security Policy)。这两种类型的信息都需要储存在内核的XFRM(Transform)中。核心XFRM使用netns_xfrm结构来组织这些信息,也被称为xfrm实例( instance)(例子)。从名称可以看出,该实例与网络命名空间(network namespace)相关,每个命名空间都有一个彼此独立的实例。所以同一主机上的不同容器可以在没有干扰的情况下使用XFRM。
struct net
{
......
#ifdef CONFIG_XFRM
struct netns_xfrm xfrm;
#endif
......
}
Netlink通道
上面提到了安全关联和安全策略信息,它们通常由用户模式 IPsec 进程发送到内核 XFRM(例如StrongSwan),这是在初始化网络命名空间时创建的。
static int __net_init xfrm_user_net_init(struct net *net)
{
struct sock *nlsk;
struct netlink_kernel_cfg cfg = {
.groups = XFRMNLGRP_MAX,
.input = xfrm_netlink_rcv,
};
nlsk = netlink_kernel_create(net, NETLINK_XFRM, &cfg);
......
return 0;
}
这样,当用户下载IPsec配置时,内核可以调用xfrm_netlink_rcv()来接收。
XFRM 状态
XFRM使用xfrm_state来表示IPsec堆栈中的安全关联(Security Association)。它包含了所有单向IPsec流量所需的信息,包括模式(传输或隧道)、密钥、替换参数等。用户态IPsec进程通过发送 XFRM_MSG_NEWSA 请求,允许XFRM创建一个xfrm_state结构。
xfrm_state包含许多字段,这里没有列出所有字段,只列出了最重要的字段:
- Id: 它是一个xfrm_id结构,包含SA的目标地址、SPI和协议(AH/ESP)。
- props: 表示SA的其他属性,包括IPsec模式(传输/隧道)、源地址等信息。
每个xfrm_state都会添加到内核中的多个哈希表中,因此内核可以从多个特性中找到相同的SA:
- xfrm_state_lookup():通过指定SPI信息查找SA。
- xfrm_state_lookup_byaddr():通过源地址查找SA。
- xfrm_state_find():通过目标地址查找SA。
用户可以通过 ip xfrm state 命令列出当前主机上的xfrm_state。
src 192.168.0.1 dst 192.168.0.2
proto esp spi 0xc420a5ed(3290473965) reqid 1(0x00000001) mode tunnel
replay-window 0 seq 0x00000000 flag af-unspec (0x00100000)
auth-trunc hmac(sha256) 0xa65e95de83369bd9f3be3afafc5c363ea5e5e3e12c3017837a7b9dd40fe1901f (256 bits) 128
enc cbc(aes) 0x61cd9e16bb8c1d9757852ce1ff46791f (128 bits)
anti-replay context: seq 0x0, oseq 0x1, bitmap 0x00000000
lifetime config:
limit: soft (INF)(bytes), hard (INF)(bytes)
limit: soft (INF)(packets), hard (INF)(packets)
expire add: soft 1004(sec), hard 1200(sec)
expire use: soft 0(sec), hard 0(sec)
lifetime current:
84(bytes), 1(packets)
add 2019-09-02 10:25:39 use 2019-09-02 10:25:39
stats:
replay-window 0 replay 0 failed 0
XFRM 策略
XFRM 使用 xfrm_policy 在协议栈中表示 IPsec 安全策略。通过发布此类规则,用户可以允许 XFRM 或禁止发送和接收具有特定特征的流。当用户的 IPsec 进程发送一个 XFRM_MSG_NEWPOLICY 请求时,可以创建一个 xfrm_policy 结构。
struct xfrm_policy {
......
struct hlist_node bydst;
struct hlist_node byidx;
/* This lock only affects elements except for entry. */
rwlock_t lock;
atomic_t refcnt;
struct timer_list timer;
struct flow_cache_object flo;
atomic_t genid;
u32 priority;
u32 index;
struct xfrm_mark mark;
struct xfrm_selector selector;
struct xfrm_lifetime_cfg lft;
struct xfrm_lifetime_cur curlft;
struct xfrm_policy_walk_entry walk;
struct xfrm_policy_queue polq;
u8 type;
u8 action;
u8 flags;
u8 xfrm_nr;
u16 family;
struct xfrm_sec_ctx *security;
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
struct rcu_head rcu;
};
这个结构体有很多字段,但大部分并不需要关注。我们应该关注下面列出的这些字段。
- selector:表示策略匹配的流的特征。
- action:取值为XFRM_POLICY_ALLOW(0)或XFRM_POLICY_BLOCK(1),前者表示允许流量通过,后者则表示禁止流量通过。
- Xfrm_nr:表示与此策略相关联的模板数量。模板可被理解为 xfrm_state 的简化版本。xfrm_nr 确定流量转换的次数。通常此值为 1。
- xfrm_vec:表示与此策略相关联的模板。数组的每个元素都是 xfrm_tmpl。可以将xfrm_tmpl解析为已完成状态。
与 xfrm_state 类似,用户可以通过 ip xfrm policies 命令列出当前主机上的xfrm_policy
src 10.1.0.0/16 dst 10.2.0.0/16 uid 0
dir out action allow index 5025 priority 383615 ptype main share any flag (0x00000000)
lifetime config:
limit: soft (INF)(bytes), hard (INF)(bytes)
limit: soft (INF)(packets), hard (INF)(packets)
expire add: soft 0(sec), hard 0(sec)
expire use: soft 0(sec), hard 0(sec)
lifetime current:
0(bytes), 0(packets)
add 2019-09-02 10:25:39 use 2019-09-02 10:25:39
tmpl src 192.168.0.1 dst 192.168.0.2
proto esp spi 0xc420a5ed(3290473965) reqid 1(0x00000001) mode tunnel
level required share any
enc-mask ffffffff auth-mask ffffffff comp-mask ffffffff
接收和发送 IPsec 消息
接收
以下图示展示了 XFRM 框架接收 IPsec 消息的过程:
总体而言,IPsec 消息的接收是一个迂回的过程。当 IP 层接收到一条消息时,根据消息的协议字段,如果是 IPsec 类型(AH、ESP),它将进入 XFRM 框架进行接收。在这个过程中,更重要的过程是 xfrm_state_lookup(),它会寻找安全关联(SA),如果找到,则会再次进行路由。根据不同的协议和模式,会进入不同的处理过程。最后,原始消息的信息将被检索并重新进入 ip_local_delivery()。然后,它需要经过 XFRM 策略过滤,最终上传到应用层。
发送
以下图示展示了 XFRM 框架发送 IPsec 消息的过程:
XFRM 在消息路由查找后发现 SA 是否满足要求。如果没有,则直接进入 ip_output(),否则进入 XFRM 进程,并根据模式和协议进行相应的处理。最后,它通过不同的路由返回到 ip_output()。
REF
- Linux Kernel Networing Implementation and Throry
翻译:https://programmer.ink/think/xfrm-a-kernel-implementation-framework-of-ipsec-protocol.html