【转载】 走进Linux内核之XFRM框架

初次发表掘金

笔者此前对Linux内核相关模块稍有研究,实现内核级通信加密、视频流加密等,涉及:Linux内核网络协议栈、Linux内核通信模块、Linux内核加密模块、秘钥生成分发等。
后续考虑开设Linux内核专栏。



在此前的文章中讨论了Linux内核中Netfilter子系统的基本架构及其实现原理,本篇文章将讨论Linux内核另一个重要的子系统——XFRM框架。

下面开始上才艺,带你走进Linux内核之XFRM框架。


概述:什么是XFRM框架

XFRM的正确读音是transform(转换), 这表示内核协议栈收到的IPsec报文需要经过转换才能还原为原始报文;

同样地,要发送的原始报文也需要转换为IPsec报文才能发送出去。

IPsec(Internet协议安全)应该很多人都听过,IPsec是一组协议,他们通过对通信会话中的每个数据包进行身份验证和加密,以确保IP流量的安全。

XFRM框架是IPsec的“基础设施”,IPsec通过XFRM框架实现的。XFRM源自USAGI项目,该项目旨在提供适用于生产环境的IPv6和IPsec协议栈。自内核2.5之后引入了XFRM框架,这个“基础设施”独立于协议簇,包含可同时应用于IPv4和IPv6的通用部分,位于源代码的net/xfrm/目录下。

XFRM框架支持网络命名空间。这是一种轻型的进程虚拟化,它可以使得一个或者一组进程有属于自己的网络栈。每个网络命名空间都含有一个名为xfrm的成员——一个netns_xfrm结构实例。这个对象包含着许多的数据结构和变量,例如:XFRM策略散列表、XFRM状态散列表、sysctl参数、XFRM状态垃圾收集器、计数器等。

netns_xfrm结构体定义,文件路径include/net/netns/xfrm.h

struct netns_xfrm {
        struct hlist_head       *state_bydst;
        struct hlist_head       *state_bysrc;
        struct hlist_head       *state_byspi;
        . . .
        unsigned int            state_num;
        . . .
    <span class="token keyword">struct</span> <span class="token class-name">work_struct</span>      state_gc_work<span class="token punctuation">;</span>

    <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span>

    u32                     sysctl_aevent_etime<span class="token punctuation">;</span>
    u32                     sysctl_aevent_rseqth<span class="token punctuation">;</span>
    <span class="token keyword">int</span>                     sysctl_larval_drop<span class="token punctuation">;</span>
    u32                     sysctl_acq_expires<span class="token punctuation">;</span>

};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

XFRM初始:XFRM Init

在IPv4中,XFRM初始化是通过在ip_rt_init()函数(位于net/ipv4/route.c文件)调用相关函数完成,函数调用结构为:ip_rt_init()->xfrm4_init()->xfrm_init()

而在IPv6中,在ipv6_route_init()函数中调用xfrm6_init()方法实现了XFRM的初始化。

用户空间和内核之间的通信创建NETLINK_XFRM类型netlink套接字(socket)以及发送和接收netlink消息来完成。内核NETLINK_XFRM Netlink套接字是在下面的函数中完成创建。

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 <span class="token operator">=</span> <span class="token function">netlink_kernel_create</span><span class="token punctuation">(</span>net<span class="token punctuation">,</span> NETLINK_XFRM<span class="token punctuation">,</span> <span class="token operator">&amp;</span>cfg<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span>
    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

从用户空间发出的消息(比如XFRM_MSG_NEWPOLICY创建新的安全策略或者XFRM_MSG_NEWSA创建新的安全联盟)会被xfrm_netlink_rcv()方法处理,该方法又会被xfrm_user_rcv_msg()方法调用(第二章曾讨论过netlink套接字)。

XFRM策略和XFRM状态是XFRM框架的基础数据结构,接下来我将陆续介绍什么是XFRM策略以及XFRM状态

XFRM策略:XFRM Policy

安全策略是一种规则,告诉IPsec一条特定流量是否应该处理或者旁路,xfrm_policy结构用来描述IPsec策略。一个安全策略包含一个选择器(一个xfrm_selector对象)。当其选择器匹配一条流时会提供一种策略。XFRM选择器有一系列属性组成:比如source和destination address、source和destination port、protocol等等,用这些属性来识别一条流:

文件路径:include/uapi/linux/xfrm.h

struct xfrm_selector {
        xfrm_address_t  daddr;
        xfrm_address_t  saddr;
        __be16  dport;
        __be16  dport_mask;
        __be16  sport;
        __be16  sport_mask;
        __u16   family;
        __u8    prefixlen_d;
        __u8    prefixlen_s;
        __u8    proto;
        int     ifindex;
        __kernel_uid32_t        user;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

xfrm_selector_match()方法使用XFRM selector、flow和family(IPv4对应AF_INET,IPv6对应AF_INET6)作为参数,当特定XFRM流量匹配中特定选择器时返回true。注意xfrm_selector结构同样用在XFRM状态中。

安全策略(Security Policy)使用xfrm_policy结构表示,xfrm_policy结构用于描述SP在内核内部的具体实现:

文件路径:include/net/xfrm.h

struct xfrm_policy
{
 struct xfrm_policy *next; // 下一个策略
 struct hlist_node bydst; // 按目的地址HASH的链表
 struct hlist_node byidx; // 按索引号HASH的链表
 /* This lock only affects elements except for entry. */
 rwlock_t  lock;  // 策略结构锁
 atomic_t  refcnt; // 引用次数
 struct timer_list timer; // 策略定时器
 u8   type;     // 类型
 u32   priority; // 策略优先级
 u32   index;    // 策略索引号
 struct xfrm_selector selector; // 选择子
 struct xfrm_lifetime_cfg lft;     // 策略生命期
 struct xfrm_lifetime_cur curlft;  // 当前的生命期数据
 struct dst_entry       *bundles;  // 路由链表
 __u16   family;   // 协议族
 __u8   action;   // 策略动作, 接受/加密/阻塞...
 __u8   flags;    // 标志
 __u8   dead;     // 策略死亡标志
 __u8   xfrm_nr;  // 使用的xfrm_vec的数量
 struct xfrm_sec_ctx *security; // 安全上下文
 struct xfrm_tmpl        xfrm_vec[XFRM_MAX_DEPTH]; // 状态模板
};
  • 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

这个结构的字段很多,但大部分并不用关心,我们重点关注下面列举出的这几个字段就行:

  • selector:表示该Policy匹配的流的特征
  • action:取值为XFRM_POLICY_ALLOW(0)或XFRM_POLICY_BLOCK(1),前者表示允许该流量,后者表示不允许。
  • xfrm_nr: 表示与这条Policy关联的template的数量,template可以理解为xfrm_state的简化版本,xfrm_nr决定了流量进行转换的次数,通常这个值为1
  • xfrm_vec: 表示与这条Policy关联的template,数组的每个元素是xfrm_tmpl, 一个xfrm_tmpl可以还原(resolve)成一个完成state.

用户可以通过下面命令,列出当前主机上的xfrm_policy

ip xfrm policy ls

 
 
  • 1
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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

XFRM状态:XFRM State

结构xfrm_state表示IPsec安全关联(include/net/xfrm.h)。它表示的是单向流量,包含加密密钥、标志、请求ID、统计信息、重放参数等信息。要添加XFRM状态,可从用户空间套接字发送请求XFRM_MSG_NEWSA,在内核中,这种请求方法由xfrm_state_add()处理(位于文件net/xfrm/xfrm_user.c)。同样,要删除状态,可发送XFRM_MSG_NEWSAXFRM_MSG_DELSA消息,在内核中,这种请求方法由xfrm_del_sa()处理。
xfrm_state状态结构用来描述SA在内核中的具体实现:

struct xfrm_state\
{\
 /* Note: bydst is re-used during gc */\
// 每个状态结构挂接到三个HASH链表中\
 struct hlist_node bydst; // 按目的地址HASH\
 struct hlist_node bysrc; // 按源地址HASH\
 struct hlist_node byspi; // 按SPI值HASH

atomic_t refcnt; // 所有使用计数
spinlock_t lock; // 状态锁

struct xfrm_id id; // ID结构, 即目的地址,SPI,协议三元组
struct xfrm_selector sel; // 状态选择子

u32 genid; // 状态的标志值, 防止发生碰撞

/* Key manger bits */
struct {
u8 state;
u8 dying;
u32 seq;
} km; // KEY回调管理处理结构参数

/* Parameters of this state. */
struct {
u32 reqid; // 请求ID
u8 mode; // 模式: 传输/通道
u8 replay_window; // 回放窗口
u8 aalgo, ealgo, calgo; // 认证,加密,压缩算法ID值
u8 flags; // 一些标准
u16 family; // 协议族
xfrm_address_t saddr; // 源地址
int header_len; // 添加的协议头长度
int trailer_len; //
} props; // SA相关参数结构

struct xfrm_lifetime_cfg lft; // 生存时间配置

/* Data for transformer /
struct xfrm_algo aalg; // hash算法
struct xfrm_algo *ealg; // 加密算法
struct xfrm_algo *calg; // 压缩算法

/* Data for encapsulator /
struct xfrm_encap_tmpl encap; // NAT-T封装信息

/* Data for care-of address /
xfrm_address_t coaddr;

/* IPComp needs an IPIP tunnel for handling uncompressed packets /
struct xfrm_state tunnel; // 通道, 实际是另一个SA

/* If a tunnel, number of users + 1 */
atomic_t tunnel_users; // 通道的使用数

/* State for replay detection */
struct xfrm_replay_state replay; // 回放检测结构,包含各种序列号掩码等信息

/* Replay detection state at the time we sent the last notification */
struct xfrm_replay_state preplay; // 上次的回放记录值

/* internal flag that only holds state for delayed aevent at the\

  • moment
    */
    u32 xflags; // 标志

/* Replay detection notification settings */
u32 replay_maxage; // 回放最大时间间隔
u32 replay_maxdiff; // 回放最大差值

/* Replay detection notification timer */
struct timer_list rtimer; // 回放检测定时器

/* Statistics */
struct xfrm_stats stats; // 统计值

struct xfrm_lifetime_cur curlft; // 当前时间计数器
struct timer_list timer; // SA定时器

/* Last used time */
u64 lastused; // 上次使用时间

/* Reference to data common to all the instances of this\

  • transformer. /
    struct xfrm_type
    type; // 协议, ESP/AH/IPCOMP
    struct xfrm_mode *mode; // 模式, 通道或传输

/* Security context /
struct xfrm_sec_ctx security; // 安全上下文, 加密时使用

/* Private data of this transformer, format is opaque,\

  • interpreted by xfrm_type methods. /
    void
    data; // 内部数据
    };
  • 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
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

xfrm_state包含的字段很多,这里就不贴了,仅仅列出其中最重要的字段:

  • id: 它是一个xfrm_id结构,包含该SA的目的地址、SPI、和协议(AH/ESP)
  • props:表示该SA的其他属性,包括IPsec Mode(Transport/Tunnel)、源地址等信息

每个xfrm_state在内核中会加入多个哈希表,因此,内核可以从多个特征查找到同一个个SA:

  • xfrm_state_lookup(): 通过指定的SPI信息查找SA
  • xfrm_state_lookup_byaddr(): 通过源地址查找SA
  • xfrm_state_find(): 通过目的地址查找SA

用户可以通过下面命令,列出当前主机上的xfrm_state

ip xfrm state ls

 
 
  • 1
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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

XFRM模板:XFRM TMPL

xfrm模板结构, 用于状态和策略的查询:

struct xfrm_tmpl\
{\
/* id in template is interpreted as:\
 * daddr - destination of tunnel, may be zero for transport mode.\
 * spi   - zero to acquire spi. Not zero if spi is static, then\
 *    daddr must be fixed too.\
 * proto - AH/ESP/IPCOMP\
 */\
// SA三元组, 目的地址, 协议, SOI\
 struct xfrm_id  id;

/* Source address of tunnel. Ignored, if it is not a tunnel. */
// 源地址
xfrm_address_t saddr;

// 请求ID
__u32 reqid;

/* Mode: transport, tunnel etc. */
__u8 mode;

/* Sharing mode: unique, this session only, this user only etc. */
__u8 share;

/* May skip this transfomration if no SA is found */
__u8 optional;

/* Bit mask of algos allowed for acquisition */
__u32 aalgos;
__u32 ealgos;
__u32 calgos;
};

  • 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

扩展阅读

《精通Linux内核》

掘金:走进Linux内核之Netfilter框架

掘金:走进Linux内核之XFRM框架

Linux内核网络:实现和理论

Linux内核中的IPSEC实现

xfrm框架

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值