[ipsec][strongswan] strongswan源码分析--(一)SA整体分析

strongswan SA分析(一)

Author: Cao Tong
Date: 20181224
Version: v1.1

1 概念

下面主要介绍两个本文将要阐述的核心概念。他们是SA和SP。注意,这不是一篇不需要背景知识的文章。作者认为你适合阅读接下来内容的的前提是,你已经具备了一下三方面的知识:

  • a. 什么是VPN。
  • b. 什么是IPsec,包括IKE,ESP,strongswan都是什么等。
  • c. 一般的linux使用方法和常见概念。

1.1 什么是SAD,SPD

SAD是Security Association Database的缩写。
SPD是Security Policy Database的缩写。
SAD是用来存储SA的数据库。SPD是用来存储SP的数据库。

1.2 什么是SPI

SPI是Security Parameter Index的缩写。是有一组数字(长度?)。被使用在SAD和SPD里作为索引的一部分。是由IKE协商的两侧客户端随机选择的UUID?。0-255是被保留的值,禁止在SPI中使用。

1.3 什么是SA

SA是Security Association的缩写。SA是一组算法和算法参数(包括key)的集合,用来完成单个方向的数据流加密和验证任务。通过SPI加数据包的目的地址可以唯一查找到一个SA。

包含的属性:

  • 加密算法
    • 属性
    • key
  • 验证算法
    • 属性
    • key
  • SPI
  • 目的地址

1.4 什么是SP

SP是Security Policy的缩写。SP是一条规则,决定一条流(flow)是否需要被IPsec处理。SP的处理有三种方式:

  • 丢弃
  • 不处理
  • 处理

需要被IPsec处理的流,会被指向到一个template。一个template可以理解为指向一个SA,template包含以下属性:

  • 协议
    • AH或ESP。
  • 模式
    • transport或tunnel模式。
  • pattern
    • 源IP加目的IP对。
    • NAT的PORT对。

SP有一个方向属性,取值分别为:

  • out
  • in
  • fwd

1.5 总结

在整个IPsec的数据流转逻辑中,SP用来表达What todo。SA用来表达How todo。


2 数据流

简单的说。明文报在通过IPsec VPN设备变成ESP发出去的过程是:

  1. 查找路由。
  2. 查找policy决定是否需要被ESP
  3. 查找SA并加密封装。
  4. 加密封装后的包再查路由。

IPsec报在通过IPsec VPN设备变成非加密包发出去的过程:

  1. 查找路由。
  2. 查找policy决定是否需要要解ESP
  3. 查找SA并解密解封装。
  4. 解密解封装后的包再查路由。

flow.png

2.1 举个栗子

路由

[root@T9 sbin]# ip route
default via 192.168.7.1 dev eth0  proto static  metric 100 
10.129.0.0/24 dev eth1  proto kernel  scope link  src 10.129.0.1  metric 100 
192.168.7.0/24 dev eth0  proto kernel  scope link  src 192.168.7.129  metric 100 

policy

[root@T9 sbin]# ip xfrm policy
src 10.9.0.0/16 dst 10.129.0.0/16 
    dir fwd priority 383616 ptype main 
    tmpl src 192.168.7.9 dst 192.168.7.129
        proto esp reqid 1 mode tunnel
src 10.9.0.0/16 dst 10.129.0.0/16 
    dir in priority 383616 ptype main 
    tmpl src 192.168.7.9 dst 192.168.7.129
        proto esp reqid 1 mode tunnel
src 10.129.0.0/16 dst 10.9.0.0/16 
    dir out priority 383616 ptype main 
    tmpl src 192.168.7.129 dst 192.168.7.9
        proto esp reqid 1 mode tunnel

sa

[root@T9 sbin]# ip xfrm state
src 192.168.7.129 dst 192.168.7.9
    proto esp spi 0xc42ac7f3 reqid 1 mode tunnel
    replay-window 0 flag af-unspec
    auth-trunc hmac(sha256) 0x5f7b99e.....eb20948fb2f8fc713caf2d43b4 128
    enc cbc(aes) 0x48144872d5f4f9a6a762b68785e6f265
src 192.168.7.9 dst 192.168.7.129
    proto esp spi 0xc1c8ad99 reqid 1 mode tunnel
    replay-window 32 flag af-unspec
    auth-trunc hmac(sha256) 0x7efc5d2172.....0c0dedf053b0b6ae5aa2f012 128
    enc cbc(aes) 0x808efcfaa45a543b69efe08158accaa3

3 理解linux kernel中的sa概念和管理

3.1 提供给用户的sa接口

理解kernel sa对用户展示的形态,可以帮助我们理解linux kernel对于ipsec sa的建模和抽象。对我们在VPN产品的sa模块设计中将提供帮助。

3.1.1 使用racoon配置sa

setkey add 192.168.0.1 192.168.1.2 esp 0x10001
        -m tunnel
        -E des-cbc 0x3ffe05014819ffff
        -A hmac-md5 "authentication!!"

从以上信息可以很容易开始各个参数表达的含义,其中-E代表加密算法和它的key,-A代表验证算法和它的key。0x10001为spi。

3.1.2 使用racoon配置policy

setkey spdadd 10.0.11.41/32[21] 10.0.11.33/32[any] any
        -P out ipsec esp/tunnel/192.168.0.1-192.168.1.2/require

第一行代表五元组,any代表协议。第二行代表policy的具体描述:方向,action,template。

3.1.3 总结

通过以上两个小节的描述,读者应该已经很容易的总结出了配置一个SA和一个policy所需要提供的最基本的信息了。作者将在本章的最后,对sa和poliyc所包含的所有必须信息进行一个统一的总结。
另外,通过上文的语法,我们应该能够发现,policy与sa之间的match操作,是需要一个稍负责的匹配逻辑来实现的,而不仅仅是一个简单的匹配关系。

3.2 netlink的SA接口

strongswan是目前使用两种方式与内核进行ipsec的配置交互,分别为netlink和pfkey。如官方文档所述,netlink是strongswan默认启用的,变成stable的接口方式。整个调研工作也是以netlink方式为出发点展开的,现简单介绍如下。

netlink是复用了socket方式的内核与用户态IPC方法。
这里有一篇写的非常好的文章,讲netlink为什么会产生。由于人家写的实在是太好了,我已经没有什么可写的了,只能做个概要,如下:
netlink.jpeg
因为作者图画是业余的,所以看懂的这个概要图的前提是,你必须懂得BSD socket的api如何使用。

3.2.2 接口方式

用netlink方式配置ipsec的方法。

netlink的一般用法
初始化socket

与常规的socket用法相同,只是传入参数是netlink定义的特有参数。

int socket(int domain, int type, int protocol)
bind(fd, (struct sockaddr*)&nladdr, sizeof(nladdr));
下发配置信息到kernel

使用socket的标准send,write接口将特定格式的参数下发给kernel。
参数格式如下:

struct nlmsghdr
{
  __u32 nlmsg_len;   /* Length of message */
  __u16 nlmsg_type;  /* Message type*/
  __u16 nlmsg_flags; /* Additional flags */
  __u32 nlmsg_seq;   /* Sequence number */
  __u32 nlmsg_pid;   /* Sending process PID */
};

这个参数结构体是传入参数的头部,紧接着这个头部之后的内存是真正的参数的值。它的解析方法由nlmsg_type的值来确定。它的结尾由nlmsg_len的数值来决定。

添加sa

添加sa的时候,nlmsghdr后面的参数为结构体

struct xfrm_usersa_info

nlmsg_type的值为:XFRM_MSG_NEWSA
这部分内容定义在系统文件下:

/usr/include/linux/xfrm.h

这个结构体后边,还需要追加算法部分的信息,如下:

struct xfrm_algo
struct xfrm_algo_auth
添加policy

添加policy的时候,nlmsghdr后面的参数为结构体

struct xfrm_userpolicy_info

nlmsg_type的值为:XFRM_MSG_NEWPOLICY
这部分内容定义在系统文件下:

/usr/include/linux/xfrm.h

3.3 xfrm的SA接口

3.3.1 什么是xfrm

xfrm(transform)是一个IP包转发框架。主要实现以下三部分功能:

  • IPsec protocol suite
  • IP Payload Compression Protocol
  • Mobile IPv6

3.3.2 内核代码

linux/net/xfrm/

主要函数

Xfrm_lookup()            xfrm lookup(SPD and SAD) method
Xfrm_input()             xfrm processing for an ingress packet
Xfrm_output()            xfrm processing for an egress packet
Xfrm4_rcv()              IPv4 specific Rx method
Xfrm6_rcv()              IPv6 specific Rx method
Esp_input()              ESP processing for an ingress packet
Esp_output()             ESP processing for an egress packet
Ah_output()              AH processing for an ingress packet
Ah_input()               ESP processing for an egress packet
xfrm_policy_alloc()      allocates an SPD object
Xfrm_policy_destroy()    frees an SPD object
xfrm_ policy_lookup      SPD lookup
xfrm_policy_byid()       SPD lookup based on id
Xfrm_policy_insert()     Add an entry to SPD
Xfrm_Policy_delete()     remove an entry from SPD
Xfrm_bundle_create()     creates a xfrm bundle
Xfrm_policy_delete()     releases the resources of a policy object
Xfrm_state_add()         add an entry to SAD
Xfrm_state_delete()      free and SAD object
Xfrm_state_alloc()       allocate an SAD object
xfrm_state_lookup_byaddr()     src address based SAD lookup
xfrm_state_find()        SAD look up based on dst
xfrm_state_lookup()      SAD lookup based on spi

3.3.3 API

api文件

include/uapi/linux/xfrm.h

主要的API

XFRM_MSG_NEWSA         To add a new SA to SAD
XFRM_MSG_DELSA         To delete a new SA to SAD
XFRM_MSG_GETSA         To get a new SA to SAD
XFRM_MSG_FLUSHSA       To flush SAD
XFRM_MSG_NEWPOLICY     To add a new policy to SPD
XFRM_MSG_DELPOLICY     To delete a new policy to SPD
XFRM_MSG_GETPOLICY     To get a new policy to SPD
XFRM_MSG_FLUSHPOLICY   To flush SPD

3.3.4 sa的传入参数

struct xfrm_usersa_info {
        struct xfrm_selector            sel; // 被加密网段?为啥要有这个?
        struct xfrm_id                  id; // 目的ip,spi,协议ah/esp
        xfrm_address_t                  saddr; // 源ip
        struct xfrm_lifetime_cfg        lft;
        struct xfrm_lifetime_cur        curlft;
        struct xfrm_stats               stats;
        __u32                           seq;
        __u32                           reqid;
        __u16                           family;
        __u8                            mode; // transport / tunnel
        __u8                            replay_window;
        __u8                            flags;
};  

算法参数是追加在SA结构体之后的内存块,根据不同的类型决定不同的结构。示例:

struct xfrm_algo {
        char            alg_name[64];
        unsigned int    alg_key_len;    /* in bits */
        char            alg_key[0];
};

struct xfrm_algo_auth {
        char            alg_name[64];
        unsigned int    alg_key_len;    /* in bits */
        unsigned int    alg_trunc_len;  /* in bits */
        char            alg_key[0];
};

3.3.5 policy的传入参数

struct xfrm_userpolicy_info {
        struct xfrm_selector            sel; //网段:ip,port,协议
        struct xfrm_lifetime_cfg        lft;
        struct xfrm_lifetime_cur        curlft;
        __u32                           priority; //
        __u32                           index;
        __u8                            dir;  //方向:in out fwd
        __u8                            action; // allow, block
        __u8                            flags;
        __u8                            share;
};

4 xfrm的实现

4.1 用于存储sa的内部数据结构

struct xfrm_state {
#ifdef CONFIG_NET_NS
        struct net              *xs_net;
#endif
        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;
                u32             extra_flags;
        } 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      *repl;
        /* 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;
};

会被插入两个hash表

   1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
   2. Hash table by (daddr,family,reqid) to find what SAs exist for given
      destination/tunnel endpoint. (output)

4.2 用于存储sa的内部数据结构

struct xfrm_policy {
#ifdef CONFIG_NET_NS
        struct net              *xp_net;
#endif
        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];
};        

4.3 数据结构之间的存储结构

TODO

4.4 关键函数

xfrm_lookup()
xfrm_output()
xfrm4_policy_check() // 在ipv4中被调用。

5 strongswan中的sa

5.1 概述

从IKE协议的角度上,有两个SA,一个叫IKE_SA,一个叫CHILD_SA。本章讨论的sa,特指下图中的CHILD_SA。
本篇文章,通篇讨论的SA指的都是这里的CHILD_SA。

CHILD_SA在strongswan的框架里,主要存在与两个部分。

  1. IKE协商过程。
    CHILD_SA是IKE协商过程中的输出。IKE协商过程结束后,IKE-SA Manager将CHILD_SA交个strongswan框架。
  2. IPsec隧道建立过程。
    CHILD_SA是IKE协商过程中的输入。strongswan框架将CHILD_SA交给libcharon plugin由特定的plugin与kernel通信,在kernel中完成IPsec tunnel的建立过程。
  3. IPsec在转发过程。
    这部分和strongswan的框架没有了关系,由内核完成。
      +---------------------------------+       +----------------------------+
      |          Credentials            |       |          Backends          |
      +---------------------------------+       +----------------------------+

       +------------+    +-----------+        +------+            +----------+
       |  receiver  |    |           |        |      |  +------+  | CHILD_SA |
       +----+-------+    | Scheduler |        | IKE- |  | IKE- |--+----------+
            |            |           |        | SA   |--| SA   |  | CHILD_SA |
    +-------+--+         +-----------+        |      |  +------+  +----------+
 <->|  socket  |               |              | Man- |
    +-------+--+         +-----------+        | ager |  +------+  +----------+
            |            |           |        |      |  | IKE- |--| CHILD_SA |
       +----+-------+    | Processor |--------|      |--| SA   |  +----------+
       |   sender   |    |           |        |      |  +------+
       +------------+    +-----------+        +------+

      +---------------------------------+       +----------------------------+
      |               Bus               |       |      Kernel Interface      |
      +---------------------------------+       +----------------------------+
             |                    |                           |
      +-------------+     +-------------+                     V
      | File-Logger |     |  Sys-Logger |                  //////
      +-------------+     +-------------+

5.1.1 strongswan中的plugin

上一小节提到了plugin,接下来讲解plugin
有两类plugins。一类是libstrongswan的plugin,一类是libcharon的plugin
libstrongswan的plugin主要提供加密,认证,数据库相关的功能。
libcharon的plugin主要提供“specific needs”。。。我们接下来要讨论的与sa下发相关的plugin都在
libcharon这一类里。他们包括:

  • kernel-libipsec
    用户态的转发平面,目前还处于高实验性阶段。转发性能没有kernel。主要用来满足不能使用kernel转发的场景。
  • kernel-netlink
    使用netlink接口与linux kernel的xfrm模块交互。目前输出稳定使用阶段,默认首选。
  • kernel-iph
    windows操作系统的接口。
  • kernel-pfkey
    使用pkkey接口与linux kernel的xfrm模块进行交互,高实验性阶段。
  • kernel-wfp
    windows操作系统的接口。

本文,只关心kernel-netlink的plugin。

5.2 启动过程

5.2.1 概述

strongswan的启动方式有多种。可以和各种不同的系统对接,包括systemd,networkmanager等。

  • starter
    ipsec命令使用的守护进程。用ipsec start命令,就会启动这个进程。
  • charon-nm
    networkmanager的plugin。什么是nm的plugin?
  • charon-systemd
    按照systemd的daemon style实现的一个进程。由systemd启动。
  • charon-svc
    windows的服务。

各种启动方式的最终目的都是启动最终目的都是启动charon进程。所以,最简的启动方法就是:

  • 直接运行charon进程

当然,这种方式没有daemon守护,但是功能完整。

5.2.2 调试方法

如上一小节所述。charon进程可以直接运行。所以调试的时候直接使用gdb运行charon就可以了。

# gdb `which charon`

5.2.3 starter的启动过程

starter的启动方法是通过ipsec脚本执行start命令,这样便启动了strongswan服务。

# ipsec start
ipsec脚本

源码位置

strongswan-5.7.1/src/ipsec/_ipsec

ipsec脚本解析start参数后,会执行如下命令,启动daemon进程starter

${IPSEC_DIR}/starter --daemon charon
starter进程

源码位置

strongswan-5.7.1/src/starter/starter.c

starter的主要功能是启动charon进程,并进行守护。

  • daemon的初始工作
    重定向输出,signal响应等。
  • 启动charon
  • 加载ipsec.conf中的配置。
流程图

starter.png

charon进程

charon进程运行启动成功后,启动16个子线程执行不同的job。
整个charon中的任务调度围绕着task和job两个核心概念进行。

流程图

实线代表流程图;虚线代表调用栈。
charon.png

5.2.4 systemd的启动过程

systemd的启动过程首先使用systemd的service配置脚本。然后启动systemd的charon守护进程。
最后通过守护进程启动charon进程。

systemd脚本

源码位置

strongswan-5.7.1/init/systemd-swanctl/strongswan-swanctl.service.in

service脚本在启动过程执行两个操作。

  1. 启动charon-systemd进程。
  2. 执行swanctl --load-all --noprompt命令
charon-systemd进程

源码位置

strongswan-5.7.1/src/charon-systemd/charon-systemd.c

charon-systemd进程是charon进程的另一个入口。charon-systemd进程不会在启动新的进程,charon-systemed进程就是处理业务的主进程,有systemd进行守护。
所以,charon-systemd只有main函数中的少量内容与charon不同。其他逻辑与charon进程完全相同。

流程图

charon-systemd.png

5.3 调用过程

运行过程中,与SA相关的两个部分主要就是add_sa与add_policy两个地方。
当charon进程收到一个message的时候,会以job的形式分发给standby的业务线程进行处理。
最后通过kernel对象调用kernel_interface接口中的add_sa和add_policy两个函数。接口会根据
具体注册的plugin调用各plugin的相应,add_as, add_policy函数。

例如,netlink的plugin。
在该plugin的这两个函数中,会通过netlink的接口最终调用内核的xfrm接口完成sa和policy的下发和更新等操作。
详见3.2和3.3两个章节。

5.4 数据结构

strongswan中的sa数据结构

定义在文件 kernel_ipsec.h 中,由id和data两个结构共同组成。

struct kernel_ipsec_sa_id_t {
        /** Source address */
        host_t *src;
        /** Destination address */
        host_t *dst;
        /** SPI */
        uint32_t spi;
        /** Protocol (ESP/AH) */
        uint8_t proto;
        /** Optional mark */
        mark_t mark;
};

/** 
 * Data required to add an SA to the kernel
 */
struct kernel_ipsec_add_sa_t {
        /** Reqid */
        uint32_t reqid;
        /** Mode (tunnel, transport...) */
        ipsec_mode_t mode;
        /** List of source traffic selectors */
        linked_list_t *src_ts;
        /** List of destination traffic selectors */
        linked_list_t *dst_ts;
        /** Network interface restricting policy */
        char *interface;
        /** Lifetime configuration */
        lifetime_cfg_t *lifetime;
        /** Encryption algorithm */
        uint16_t enc_alg;
        /** Encryption key */
        chunk_t enc_key;
        /** Integrity protection algorithm */
        uint16_t int_alg;
        /** Integrity protection key */
        chunk_t int_key;
        /** Anti-replay window size */
        uint32_t replay_window;
        /** Traffic Flow Confidentiality padding */
        uint32_t tfc;
        /** IPComp transform */
        uint16_t ipcomp;
        /** CPI for IPComp */
        uint16_t cpi;
        /** TRUE to enable UDP encapsulation for NAT traversal */
        bool encap;
        /** no (disabled), yes (enabled), auto (enabled if supported) */
        hw_offload_t hw_offload;
        /** Mark the SA should apply to packets after processing */
        mark_t mark;
        /** TRUE to use Extended Sequence Numbers */
        bool esn;
        /** TRUE to copy the DF bit to the outer IPv4 header in tunnel mode */
        bool copy_df;
        /** TRUE to copy the ECN header field to/from the outer header */
        bool copy_ecn;
        /** Whether to copy the DSCP header field to/from the outer header */
        dscp_copy_t copy_dscp;
        /** TRUE if initiator of the exchange creating the SA */
        bool initiator;
        /** TRUE if this is an inbound SA */
        bool inbound;
        /** TRUE if an SPI has already been allocated for this SA */
        bool update;
}; 

strongswan中的policy数据结构

定义在文件 kernel_ipsec.h 和 ipsec_types.h 中。

struct kernel_ipsec_policy_id_t {
        /** Direction of traffic */
        policy_dir_t dir;
        /** Source traffic selector */
        traffic_selector_t *src_ts;
        /** Destination traffic selector */
        traffic_selector_t *dst_ts;
        /** Optional mark */
        mark_t mark; 
        /** Network interface restricting policy */
        char *interface;
};

/**
 * Data required to add/delete a policy to/from the kernel
 */
struct kernel_ipsec_manage_policy_t {
        /** Type of policy */
        policy_type_t type;
        /** Priority class */
        policy_priority_t prio;
        /** Manually-set priority (automatic if set to 0) */
        uint32_t manual_prio;
        /** Source address of the SA(s) tied to this policy */
        host_t *src;
        /** Destination address of the SA(s) tied to this policy */
        host_t *dst;
        /** Details about the SA(s) tied to this policy */
        ipsec_sa_cfg_t *sa;
};

struct ipsec_sa_cfg_t {
    /** mode of SA (tunnel, transport) */
    ipsec_mode_t mode;
    /** unique ID */
    uint32_t reqid;
    /** number of policies of the same kind (in/out/fwd) attached to SA */
    uint32_t policy_count;
    /** details about ESP/AH */
    struct {
        /** TRUE if this protocol is used */
        bool use;
        /** SPI for ESP/AH */
        uint32_t spi;
    } esp, ah;
    /** details about IPComp */
    struct {
        /** the IPComp transform used */
        uint16_t transform;
        /** CPI for IPComp */
        uint16_t cpi;
    } ipcomp;
};

6 sa的抽象模型

6.1 实现sa管理的思路

我们自己的sa管理功能可以通过实现一个libcharon plugin的方式来完成。将这个plugin通过strongswan的标准接口注册进它的框架里。

然后通过该plugin与我们自己的dpdk数据面转发接口进行通信。

6.2 sa

目的地址(dip)加 spi 唯一确定一个sa条目。
| 属性 | 取值 | 说明 |
| ---- | ---- | ---- |
| id
| spi | | 协商过程带过来的 |
| mode | transport/tunnel |
| protocol | esp/ah/ipcom | 加密协议的方式
| sip | | 另一条隧道是sip和dip互换的,故两个sa
| dip
| life | | 生存时间 |
| enc_alg
| enc_key
| integrity_alg | | 完整性验证 |
| integrity_key
| nat | | 是否做nat |

6.3 policy

属性 取值 说明
id
action drop/pass/ipsec 命中此策略后的行为
priority 优先级
dir in/out/fwd 方向
s_ts source traffic selector
d_ts destination traffic selector

6.4 traffic selector

ts就是五元组,ip使用掩码掩起来的一个段。port也可以掩,具体跟kernel学一下。
| 属性 | 说明 |
| ---- | ---- |
| source ip
| sip_prefixlen
| dest ip
| dip_prefixlen
| sport
| sport_mask
| dport
| dport_mask
| protocol

7 问题

7.1 policy与路由的关系

在我的测试虚机环境里,删掉了策略路由之后,功能正常。
目前还不清楚为什么。路由与policy之间的关系,以及路由和policy在内核包转发过程中的逻辑关系,
都需要进一步的调研。

7.2 policy与sa之间的关联逻辑


参考

http://man7.org/linux/man-pages/man8/ip-xfrm.8.html

猜你喜欢

转载自www.cnblogs.com/hugetong/p/11143366.html