XFRM-IPsec内核实现框架

目录

XFRM介绍

XFRM初始化

XFRM Policies

XFRM States (Security Associations)


XFRM介绍

IPsecXFRM(发音为transfrom)框架实现,起源于USAGI项目,旨在提供一种产品级的IPv6IPsec协议栈。transform就是指根据一些IPsec规则在内核协议栈传输进来的包和出去的包。XFRM框架在Linux内核2.5版本被引用进来。XFRM基础结构是独立于协议族的,也就是说对于IPv4和IPv6来说是个通用独立的部分,在net/xfrm目录下可见。针对ESP,AH和IPCOMP,IPv4和IPv6都有他们自己的实现,例如IPv4的ESP模块是net/ipv4/esp4.c, 而IPv6的ESP模块是net/ipv6/esp6.c。除此之外,IPv4和IPv6为了支持XFRM基础结构实现了自己特有的模块,比如net/ipv4/xfrm4_policy.cnet/ipv6/xfrm_poilcy.c

XFRM框架支持网络命名空间,一种轻量级进程虚拟化可以使能一个单独的进程或者一组进程拥有他们自己的网络协议栈。每一个网络命名空间(struct net的实例)包括了一个成员叫xfrm,xfrm是结构体netns_xfrm的实例,该结构体如下:

struct netns_xfrm {
    struct hlist_head *state_bydst;
    struct hlist_head *state_bysrc;
    struct hlist_head *state_byspi;
    . . .
    unsigned int state_num;
    . . .

    struct work_struct state_gc_work;

    . . .

    u32 sysctl_aevent_etime;
    u32 sysctl_aevent_rseqth;
    int sysctl_larval_drop;
    u32 sysctl_acq_expires;
};

(include/net/netns/xfrm.h)

struct net {

    . . .

#ifdef CONFIG_XFRM
        struct netns_xfrm       xfrm;
#endif
    . . .

};

(include/net/net_namespace.h)

XFRM初始化

在IPV4中, XFRM初始化是通过调用xrm_init()来完成的。xfrm4_init()方法又被ip_rt_init()方法调用,见实现代码net/ipv4/route.c内。在IPv6中,xfrm6_init()方法在ip6_route_init()中调用来实现XFRM的初始化。用户空间和内核空间之间的通信是通过创建NETLINK_XFRM的netlink套接字,并发送接收netlink消息来实现的。netlink的NETLINK_XFRM内核态套接字是通过下面的方法创建的。

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;
}

从用户空间发送的消息(像XFRM_MSG_NEWPOLICY来创建一个新的安全策略或XFRM_MSG_NEWSA来创建一个新的安全关联)是被xfrm_netlink_rcv()方法处理的(net/xfrm/xfrm_user.c),接着调用xfrm_user_rcv_msg()方法。

XFRM policyXFRM state是XFRM框架的基础数据结构。我将先描述什么是XFRM policy,随后描述什么是XFRM state

XFRM Policies

一个安全策略是一条规则,告诉IPsec是否一条相关的流应该被处理或者旁路于IPsec处理机制。xfrm_policy结构体代表了一条IPsec策略。一条策略包括了一个选择器selector(一个xfrm_selector对象).当它的选择器匹配到一条流的时候,这条策略就会被应用。XFRM选择器由这些域组成像源地址和目的地址,源端口和目的端口,协议等等。具体结构体如下:

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;
};

(include/uapi/linux/xfrm.h)

xfrm_selector_match()方法的输入参数为XFRM selector,一条流,一个地址组参数(IPv4填AF_INET,IPv6填AF_INET6),当指定的流匹配到指定的XFRM selector的时候,函数返回TRUE。注意的是,xfrm_selector结构体也被XFRM states使用,随后你就能看到。一条安全策略是被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];
};

(include/net/xfrm.h)

接下来介绍xfrm_policy结构体的一些重要成员:

.refcnt: XFRM策略引用计数;在xfrm_policy_alloc函数里初始化为1, xfrm_pol_hold函数来加1, xfrm_pol_put函数来减1.

· timer: Per-policy timer; the timer callback is set to be xfrm_policy_timer() in the xfrm_policy_alloc() method. The xfrm_policy_timer() method handles policy expiration: it is responsible for deleting a policy when it is expired by calling thexfrm_policy_delete() method, and sending an event (XFRM_MSG_POLEXPIRE) to all registered Key Managers by calling the km_policy_expired() method.

.timer:定时器;定时器的回调函数在xfrm_policy_alloc()函数里被设置为xfrm_policy_timer()方法。

· lft: The XFRM policy lifetime (xfrm_lifetime_cfg object). Every XFRM policy has a lifetime, which is a time interval (expressed as a time or byte count).

You can set XFRM policy lifetime values with the ip command and the limit parameter—for example:

ip xfrm policy add src 172.16.2.0/24 dst 172.16.1.0/24 limit byte-soft 6000 ...

· sets the soft_byte_limit of the XFRM policy lifetime (lft) to be 6000; see man 8 ip xfrm.

You can display the lifetime (lft) of an XFRM policy by inspecting the lifetime configuration entry when running ip -stat xfrm policy show.

· curlft: The XFRM policy current lifetime, which reflects the current status of the policy in context of lifetime. The curlft is an xfrm_lifetime_cur object. It consists of four members (all of them are fields of 64 bits, unsigned):

· bytes: The number of bytes which were processed by the IPsec subsystem, incremented in the Tx path by the xfrm_output_one() method and in the Rx path by the xfrm_input() method.

· packets: The number of packets that were processed by the IPsec subsystem, incremented in the Tx path by the xfrm_output_one() method, and in the Rx path by the xfrm_input() method.

· add_time: The timestamp of adding the policy, initialized when adding a policy, in the xfrm_policy_insert() method and in the xfrm_sk_policy_insert() method.

· use_time: The timestamp of last access to the policy. The use_time timestamp is updated, for example, in the xfrm_lookup() method or in the __xfrm_policy_check() method. Initialized to 0 when adding the XFRM policy, in thexfrm_policy_insert() method and in the xfrm_sk_policy_insert() method.

Note You can display the current lifetime (curlft) object of an XFRM policy by inspecting the lifetime current entry when running ip -stat xfrm policy show.

· polq: A queue to hold packets that are sent while there are still no XFRM states associated with the policy. As a default, such packets are discarded by calling the make_blackhole() method. When setting the xfrm_larval_drop sysctl entry to 0 (/proc/sys/net/core/xfrm_larval_drop), these packets are kept in a queue (polq.hold_queue) of SKBs; up to 100 packets (XFRM_MAX_QUEUE_LEN) can be kept in this queue. This is done by creating a dummy XFRM bundle, by thexfrm_create_dummy_bundle() method (see more in the “XFRM lookup” section later in this chapter). By default, the xfrm_larval_drop sysctl entry is set to 1 (see the __xfrm_sysctl_init() method in net/xfrm/xfrm_sysctl.c).

· type: Usually the type is XFRM_POLICY_TYPE_MAIN (0). When the kernel has support for subpolicy (CONFIG_XFRM_SUB_POLICY is set), two policies can be applied to the same packet, and you can use the XFRM_POLICY_TYPE_SUB (1) type. Policy that lives a shorter time in kernel should be a subpolicy. This feature is usually needed only for developers/debugging and for mobile IPv6, because you might apply one policy for IPsec and one for mobile IPv6. The IPsec policy is usually the main policy with a longer lifetime than the mobile IPv6 (sub) policy.

· action: Can have one of these two values:

· XFRM_POLICY_ALLOW (0): Permit the traffic.

· XFRM_POLICY_BLOCK(1): Disallow the traffic (for example, when using type=reject or type=drop in /etc/ipsec.conf).

· xfrm_nr: Number of templates associated with the policy—can be up to six templates (XFRM_MAX_DEPTH). The xfrm_tmpl structure is an intermediate structure between the XFRM state and the XFRM policy. It is initialized in the copy_templates()method, net/xfrm/xfrm_user.c.

· family: IPv4 or IPv6.

· security: A security context (xfrm_sec_ctx object) that allows the XFRM subsystem to restrict the sockets that can send or receive packets via Security Associations (XFRM states). For more details, see http://lwn.net/Articles/156604/.

· xfrm_vec: An array of XFRM templates (xfrm_tmpl objects).

内核存储IPsec安全策略放在安全策略数据库(SPD)里,对安全策略数据库(SPD)的管理是通过用户态socket发送NET_LINK消息来做的。

例如:

· Adding an XFRM policy (XFRM_MSG_NEWPOLICY) is handled by the xfrm_add_policy() method.

· Deleting an XFRM policy (XFRM_MSG_DELPOLICY) is handled by the xfrm_get_policy() method.

· Displaying the SPD (XFRM_MSG_GETPOLICY) is handled by the xfrm_dump_policy() method.

· Flushing the SPD (XFRM_MSG_FLUSHPOLICY) is handled by the xfrm_flush_policy() method.

XFRM States (Security Associations)

xfrm_stat结构体代表了一个IPsec安全关联(SA)(include/net/xfrm.h)。它代表了一条单向传输并包含的信息有加密key,标记,request id,统计信息,重放参数等等。你可以用过用户态的socket发送一条XFRM_MSG_NEWSA请求来添加XFRM states。这条请求信息将被xfrm_state_add()方法(net/xfrm/xfrm_user.c)在内核里被处理。同理,你可以通过非发送XFRM_MSG_DELSA消息来删除一条state,它将在内核里被xfrm_del_sa()方法处理。

struct xfrm_state {
    . . .
    union {
        struct hlist_node gclist;
        struct hlist_node bydst;
    };

    struct hlist_node bysrc;
    struct hlist_node byspi;

    atomic_t refcnt;
    spinlock_t lock;

    struct xfrm_id id;
    struct xfrm_selector sel;
    struct xfrm_mark mark;
    u32 tfcpad;

    u32 genid;

    /* Key manager bits */
    struct xfrm_state_walk km;

    /* Parameters of this state. */
    struct {
        u32 reqid;
        u8 mode;
        u8 replay_window;
        u8 aalgo, ealgo, calgo;
        u8 flags;
        u16 family;
        xfrm_address_t saddr;
        int header_len;
        int trailer_len;
    } props;

    struct xfrm_lifetime_cfg lft;

    /* Data for transformer */
    struct xfrm_algo_auth *aalg;
    struct xfrm_algo *ealg;
    struct xfrm_algo *calg;
    struct xfrm_algo_aead *aead;

    /* Data for encapsulator */
    struct xfrm_encap_tmpl *encap;

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

    /* IPComp needs an IPIP tunnel for handling uncompressed packets */
    struct xfrm_state *tunnel;

    /* If a tunnel, number of users + 1 */
    atomic_t tunnel_users;

    /* State for replay detection */
    struct xfrm_replay_state replay;
    struct xfrm_replay_state_esn *replay_esn;

    /* Replay detection state at the time we sent the last notification */
    struct xfrm_replay_state preplay;
    struct xfrm_replay_state_esn *preplay_esn;

    /* The functions for replay detection. */
    struct xfrm_replay *reply;

    /* 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 tasklet_hrtimer mtimer;

    /* used to fix curlft->add_time when changing date */
    long saved_tmo;

    /* Last used time */
    unsigned long lastused;

    /* Reference to data common to all the instances of this
     * transformer. */
    const struct xfrm_type *type;
    struct xfrm_mode *inner_mode;
    struct xfrm_mode *inner_mode_iaf;
    struct xfrm_mode *outer_mode;

    /* Security context */
    struct xfrm_sec_ctx *security;

    /* Private data of this transformer, format is opaque,
     * interpreted by xfrm_type methods. */
    void *data;
};

(include/net/xfrm.h)

The following description details some of the important members of the xfrm_state structure:

· refcnt: A reference counter, incremented by the xfrm_state_hold() method and decremented by the __xfrm_state_put() method or by the xfrm_state_put() method (the latter also releases the XFRM state by calling the__xfrm_state_destroy() method when the reference counter reaches 0).

· id: The id (xfrm_id object) consists of three fields, which uniquely define it: destination address, spi, and security protocol (AH, ESP, or IPCOMP).

· props: The properties of the XFRM state. For example:

· mode: Can be one of five modes (for example, XFRM_MODE_TRANSPORT for transport mode or XFRM_MODE_TUNNEL for tunnel mode; see include/uapi/linux/xfrm.h).

· flag: For example, XFRM_STATE_ICMP. These flags are available in include/uapi/linux/xfrm.h. These flags can be set from userspace, for example, with the ip command and the flag option: ip xfrm add state flag icmp ...

· family: IPv4 of IPv6.

· saddr: The source address of the XFRM state.

· lft: The XFRM state lifetime (xfrm_lifetime_cfg object).

· stats: An xfrm_stats object, representing XFRM state statistics. You can display the XFRM state statistics by ip –stat xfrm show.

The kernel stores the IPsec Security Associations in the Security Associations Database (SAD). The xfrm_state objects are stored in three hash tables in netns_xfrm (the XFRM namespace, discussed earlier): state_bydst, state_bysrc, state_byspi. The keys to these tables are computed by the xfrm_dst_hash(), xfrm_src_hash(), and xfrm_spi_hash() methods, respectively. When an xfrm_state object is added, it is inserted into these three hash tables. If the value of the spi is 0 (the value 0 is not normally to be used for spi—I will shortly mention when it is 0), the xfrm_state object is not added to the state_byspi hash table (see the __xfrm_state_insert() method in net/xfrm/xfrm_state.c).

Note An spi with value of 0 is only used for acquire states. The kernel sends an acquire message to the key manager and adds a temporary acquire state with spi 0 if traffic matches a policy, but the state is not yet resolved. The kernel does not bother to send a further acquire as long as the acquire state exists; the lifetime can be configured at net->xfrm.sysctl_acq_expires. If the state gets resolved, this acquire state is replaced by the actual state.

Lookup in the SAD can be done by the following:

· xfrm_state_lookup() method: In the state_byspi hash table.

· xfrm_state_lookup_byaddr() method: In the state_bysrc hash table.

· xfrm_state_find() method: In the state_bydst hash table.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值