自动创建XFRM虚拟接口的net2net形式的IPSEC

以下根据strongswan代码中的testing/tests/route-based/net2net-xfrmi/中的测试环境,来看一下基于路由和XFRM接口的安全连接。在配置中sun网关使用特殊值%unique-dir自动为in/out两个方向创建不同ID值的xfrm虚拟设备;而moon网关仍然使用在swanctl.conf文件中手动指定xfrm接口ID值,并且手动创建XFRM接口的方法。sun网关的strongswan进程在生成xfrm接口ID之后,由updown插件调用updown脚本完成对应的设置。拓扑结构如下:

在这里插入图片描述

拓扑图中使用到两个IPSec网关设备:虚拟网关moon和sun。

sun网关配置

sun的配置文件:/etc/swanctl/swanctl.conf,内容如下。注意其中的net-net子连接配置,if_id_in和if_id_out,都指定了特殊值%unique-dir。

connections {

   gw-gw {
      local_addrs  = PH_IP_SUN
      remote_addrs = PH_IP_MOON

      local {
         auth = pubkey
         certs = sunCert.pem
         id = sun.strongswan.org
      }
      remote {
         auth = pubkey
         id = moon.strongswan.org
      }
      children {
         net-net {
            local_ts  = 0.0.0.0/0
            remote_ts = 0.0.0.0/0

            if_id_in = %unique-dir
            if_id_out = %unique-dir

            updown = /etc/updown

            esp_proposals = aes128gcm128-x25519
         }
      }
      version = 2
      proposals = aes128-sha256-x25519
   }
}

sun的脚本文件:/etc/updown,内容如下。在子连接建立/删除时,执行此脚本。由于在以上swanctl.conf文件中为if_id_in/if_id_out指定了特殊值%unique-dir,在执行此脚本时,将创建xfrmi虚拟接口,添加路由和iptables规则的操作。

在销毁时,删除xfrmi接口,删除添加的iptables规则。此脚本由strongswan的updown插件执行。

#!/bin/bash

IF_NAME="xfrm-"
IF_NAME_IN="${IF_NAME}${PLUTO_IF_ID_IN}-in"
IF_NAME_OUT="${IF_NAME}${PLUTO_IF_ID_OUT}-out"

case "${PLUTO_VERB}" in
    up-client)
        /usr/local/libexec/ipsec/xfrmi -n "${IF_NAME_OUT}" -i "${PLUTO_IF_ID_OUT}" -d eth0
        /usr/local/libexec/ipsec/xfrmi -n "${IF_NAME_IN}" -i "${PLUTO_IF_ID_IN}" -d eth0
        ip link set "${IF_NAME_OUT}" up
        ip link set "${IF_NAME_IN}" up
        ip route add 10.1.0.0/16 dev "${IF_NAME_OUT}"
        iptables -A FORWARD -o "${IF_NAME_OUT}" -j ACCEPT
        iptables -A FORWARD -i "${IF_NAME_IN}" -j ACCEPT
        ;;
    down-client)
        iptables -D FORWARD -o "${IF_NAME_OUT}" -j ACCEPT
        iptables -D FORWARD -i "${IF_NAME_IN}" -j ACCEPT
        ip link del "${IF_NAME_OUT}"
        ip link del "${IF_NAME_IN}"
        ;;
esac

xfrmi虚拟接口的名称为:IF_NAME_IN和IF_NAME_OUT,其由三部分组成:固定的xfrm-前缀,接口ID和方向标识。此处由于指定了%unique-dir特殊值,意味着两个方向将使用不同的ID值。在文件strongswan-5.8.1/src/libcharon/sa/child_sa.c文件中,函数child_sa_create创建安全关联,其中函数allocate_unique_if_ids将根据配置分配ID值。

child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config, child_sa_create_t *data)
{
        static refcount_t unique_id = 0, unique_mark = 0;

        INIT(this,
                .public = {
                        .get_unique_id = _get_unique_id,
                        .get_if_id = _get_if_id,
                },
                .if_id_in = config->get_if_id(config, TRUE) ?: data->if_id_in_def,
                .if_id_out = config->get_if_id(config, FALSE) ?: data->if_id_out_def,
        );

        if (data->if_id_in)
        {
                this->if_id_in = data->if_id_in;
        }
        if (data->if_id_out)
        {
                this->if_id_out = data->if_id_out;
        }

        allocate_unique_if_ids(&this->if_id_in, &this->if_id_out);

对于设置了IF_ID_UNIQUE_DIR标志(即%unique-dir)的in/out方向的ID值,其使用一个静态变量unique_if_id来分配id值,首先分配给in方向;其次是out方向,下文将会看到,在两个方向上都开启自动分配的情况下,in方向的ID值为1;out方向为2。ref_get函数在每次调用之后,将静态变量unique_if_id值递增一。

另外,在swanctl.conf配置文件中,也可将if_id_in/if_id_out的值设置为特殊值%unique,此配置将为in/out两个方向分配相同的ID值。

void allocate_unique_if_ids(uint32_t *in, uint32_t *out)
{       
        static refcount_t unique_if_id = 0;
        
        if (IF_ID_IS_UNIQUE(*in) || IF_ID_IS_UNIQUE(*out))
        {       
                refcount_t if_id = 0; 
                bool unique_dir = *in == IF_ID_UNIQUE_DIR || *out == IF_ID_UNIQUE_DIR;
                
                if (!unique_dir)
                {       
                        if_id = ref_get(&unique_if_id);
                }
                if (IF_ID_IS_UNIQUE(*in))
                {       
                        *in = unique_dir ? ref_get(&unique_if_id) : if_id;
                }
                if (IF_ID_IS_UNIQUE(*out))
                {       
                        *out = unique_dir ? ref_get(&unique_if_id) : if_id;
                }

在文件:strongswan-5.8.1/src/libcharon/plugins/updown/updown_listener.c文件中,函数child_updown在子SA创建和删除时调用,其子函数invoke_once如下,在调用/etc/updown脚本之前,设置环境变量PLUTO_IF_ID_IN和PLUTO_IF_ID_OUT,以便脚本中使用。

static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, ...)
{

        if_id = child_sa->get_if_id(child_sa, TRUE);
        if (if_id)
        {
                push_env(envp, countof(envp), "PLUTO_IF_ID_IN=%u", if_id);
        }
        if_id = child_sa->get_if_id(child_sa, FALSE);
        if (if_id)
        {
                push_env(envp, countof(envp), "PLUTO_IF_ID_OUT=%u", if_id);
        }

moon网关配置

moon网关的配置文件:/etc/swanctl/swanctl.conf,内容如下。注意其中的net-net子连接配置,if_id_out指定为1337,if_id_in指定了不同的值42。与以上sun网关的特殊值(%unique-dir)配置不同。

connections {

   gw-gw {
      local_addrs  = PH_IP_MOON
      remote_addrs = PH_IP_SUN

      local {
         auth = pubkey
         certs = moonCert.pem
         id = moon.strongswan.org
      }
      remote {
         auth = pubkey
         id = sun.strongswan.org
      }
      children {
         net-net {
            local_ts  = 0.0.0.0/0
            remote_ts = 0.0.0.0/0

            if_id_out = 1337
            if_id_in = 42

            esp_proposals = aes128gcm128-x25519
         }
      }
      version = 2
      proposals = aes128-sha256-x25519
   }
}

连接建立流程

操作流程如下,由于在moon网关上明确配置了if_id_in和if_id_out值,此处创建相应ID值的xfrm虚拟接口,见如下的xfrmi命令。对于sun主机由于指定的为特殊值(%unique-dir),交由strongswan进程去处理。

随后,在moon网关上配置到sun网关背后网络的路由,经由刚创建的xfrm-moon-out设备。

moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-out -d eth0 -i 1337
moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-in  -d eth0 -i 42
moon::ip link set xfrm-moon-out up
moon::ip link set xfrm-moon-in up
moon::ip route add 10.2.0.0/16 dev xfrm-moon-out
moon::iptables -A FORWARD -o xfrm-moon-out -j ACCEPT
moon::iptables -A FORWARD -i xfrm-moon-in -j ACCEPT

最后在两个虚拟网关sun和moon上启动strongswan进程,以及在moon网关上启动名称为net-net的子连接。

moon::systemctl start strongswan
sun::systemctl start strongswan
moon::expect-connection gw-gw
sun::expect-connection gw-gw
moon::swanctl --initiate --child net-net

以上的xfrmi命令将在moon网关上创建虚拟的XFRM接口:xfrm-moon-out和xfrm-moon-in,分别指定ID值(xfrm_id)为1337和42,其底层物理接口均为eth0。可使用xfrmi -list命令进行查看。

moon:~# /usr/local/libexec/ipsec/xfrmi -list
16: xfrm-moon-out    dev eth0     if_id 0x00000539 [1337]
17: xfrm-moon-in     dev eth0     if_id 0x0000002a [42]
No leaks detected, 2 suppressed by whitelist
moon:~# 

使用ip route命令查看为xfrm-moon-out虚拟接口添加的路由信息,目的网段10.2.0.0/16(bob主机所在网段)的流量路由到xfrm-moon-out接口。

moon:~# ip route
default via 192.168.0.254 dev eth0 onlink 
10.1.0.0/16 dev eth1 proto kernel scope link src 10.1.0.1 
10.2.0.0/16 dev xfrm-moon-out scope link 
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.1 
moon:~# 

以下配置的iptables规则,允许目的接口为xfrm-moon-out虚拟接口的所有流量,或者源接口为xfrm-moon-in虚拟接口的所有流量。

moon:~# iptables -L -n -v

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    1    84 ACCEPT     all  --  *      xfrm-moon-out  0.0.0.0/0            0.0.0.0/0           
    1    84 ACCEPT     all  --  xfrm-moon-in *       0.0.0.0/0            0.0.0.0/0 

以下为moon网关发起net-net子连接时,sun网关上strongswan的与xfrm有关的日志信息。

sun charon-systemd: 15[IKE] CHILD_SA net-net{1} established with SPIs c0d94a84_i c9dc4b42_o and TS 0.0.0.0/0 === 0.0.0.0/0
sun charon-systemd: 15[CHD] updown: No leaks detected, 1 suppressed by whitelist
sun charon-systemd: 15[CHD] updown: No leaks detected, 1 suppressed by whitelist
sun charon-systemd: 16[KNL] interface xfrm-2-out activated
sun charon-systemd: 07[KNL] fe80::dda6:63cc:3554:b7b0 appeared on xfrm-2-out
sun charon-systemd: 10[KNL] interface xfrm-1-in activated
sun charon-systemd: 11[KNL] fe80::5f6b:9f2f:f29:3d4c appeared on xfrm-1-in

对于sun网关,由于其if_id_in/out指定为值%unique-dir,其xfrmi虚拟接口,相关路由和iptables规则,都是由以上的updown脚本所生成。

sun:~# /usr/local/libexec/ipsec/xfrmi -list
16: xfrm-2-out       dev eth0     if_id 0x00000002 [2]
17: xfrm-1-in        dev eth0     if_id 0x00000001 [1]
No leaks detected, 2 suppressed by whitelist
sun:~# 

使用ip route命令查看为xfrm-2-out虚拟接口添加的路由信息,目的网段10.1.0.0/16(alice主机所在网段)的流量路由到xfrm-2-out接口。

sun:~# ip route
default via 192.168.0.254 dev eth0 onlink 
10.1.0.0/16 dev xfrm-2-out scope link 
10.2.0.0/16 dev eth1 proto kernel scope link src 10.2.0.1 
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2 
sun:~# 

以下配置的iptables规则,允许目的接口为xfrm-2-out虚拟接口的所有流量,或者源接口为xfrm-1-in虚拟接口的所有流量。

sun:~# iptables -L -n -v

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    1    84 ACCEPT     all  --  *      xfrm-2-out  0.0.0.0/0            0.0.0.0/0           
    1    84 ACCEPT     all  --  xfrm-1-in *       0.0.0.0/0            0.0.0.0/0   

如果在alice主机上ping网关sun后面的主机bob,报文的源地址为alice的eth0接口地址10.1.0.10,目的地址为bob的eth0接口地址10.2.0.10。报文首先到达moon网关,根据moon网关上的路由配置,将其由接口xfrm-moon-out发出。执行其发送函数:xfrmi_xmit,将流结构的成员flowi_oif出接口,替换为XFRM虚拟接口所依据的底层物理接口的索引,例如以上的eth0接口的索引。

static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct xfrm_if *xi = netdev_priv(dev);
    struct flowi fl;

    memset(&fl, 0, sizeof(fl));
	...

    fl.flowi_oif = xi->phydev->ifindex;

    ret = xfrmi_xmit2(skb, dev, &fl);

第二阶段的发送函数xfrmi_xmit2如下,其中xfrm_lookup_with_ifid函数,将根据流信息fl和if_id(此处为1337),查找匹配的xfrm策略,最终将生成xfrm_dst结构的路由信息结构,并绑定xfrm策略和状态state,最后由dst_output执行发送。

static int xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
{
    struct xfrm_if *xi = netdev_priv(dev);
    struct dst_entry *dst = skb_dst(skb);
    struct net_device *tdev;
    struct xfrm_state *x;

    dst_hold(dst);
    dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, xi->p.if_id);

    x = dst->xfrm;

    if (x->if_id != xi->p.if_id)
        goto tx_err_link_failure;
    ...

    err = dst_output(xi->net, skb->sk, skb);

此处省略sun网关的处理过程,假设bob的回复报文到达moon网关。将由如下的xfrmi_rcv_cb函数处理,其中子函数xfrmi_lookup将xfrm状态结构中的的if_id(此处为42),与保存在网络命名空间中的xfrmi虚拟接口结构中的if_id做比较,查找对应的xfrm虚拟输入接口。

static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
{
    struct xfrm_state *x;
    struct xfrm_if *xi;

    x = xfrm_input_state(skb);
    xi = xfrmi_lookup(xs_net(x), x);
    if (!xi)
        return 1;

    dev = xi->dev;
    skb->dev = dev;

static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x)
{
    struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
    struct xfrm_if *xi;

    for_each_xfrmi_rcu(xfrmn->xfrmi[0], xi) {
        if (x->if_id == xi->p.if_id && (xi->dev->flags & IFF_UP))
            return xi;

安全策略和关联

在moon网关上使用swanctl命令,可看到SA子连接信息在in/out两个方向上的接口ID值:0x0000002a(42)和0x00000539(1337),分别对应着moon的xfrmi虚拟接口xfrm-moon-in和xfrm-moon-out。

moon:~# swanctl --list-sas
gw-gw: #4, ESTABLISHED, IKEv2, 0a1b1b5c67a915ec_i ff721e588788eed3_r*
  local  'moon.strongswan.org' @ 192.168.0.1[4500]
  remote 'sun.strongswan.org' @ 192.168.0.2[4500]
  AES_CBC-128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/CURVE_25519
  established 9878s ago, rekeying in 4177s
  net-net: #16, reqid 1, INSTALLED, TUNNEL, ESP:AES_GCM_16-128/CURVE_25519
    installed 1232s ago, rekeying in 2220s, expires in 2728s
    in  c2413a11 (-|0x0000002a),      0 bytes,     0 packets
    out c56daa73 (-|0x00000539),      0 bytes,     0 packets
    local  0.0.0.0/0
    remote 0.0.0.0/0
No leaks detected, 1442 suppressed by whitelist
moon:~# 

在sun网关上使用swanctl命令的选项raw,可以更清晰的看到if-id-in和if-id-out的值,分别对应值updown脚本创建的xfrm虚拟设备:xfrm-1-in和xfrm-2-out。

sun:~# swanctl  --list-sas --raw
list-sa event {gw-gw {uniqueid=4 version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=4500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=4500 
remote-id=moon.strongswan.org initiator=yes initiator-spi=0a1b1b5c67a915ec responder-spi=ff721e588788eed3 encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 established=10125 rekey-time=3701 child-sas {net-net-16 {name=net-net uniqueid=16 reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP spi-in=c56daa73 spi-out=c2413a11 
if-id-in=00000001 if-id-out=00000002 encr-alg=AES_GCM_16 encr-keysize=128 dh-group=CURVE_25519 bytes-in=0 packets-in=0 bytes-out=0 packets-out=0 rekey-time=1908 life-time=2481 install-time=1479 local-ts=[0.0.0.0/0] remote-ts=[0.0.0.0/0]}}}}
list-sas reply {}
No leaks detected, 1442 suppressed by whitelist
sun:~# 

strongswan版本: 5.8.1
内核版本: 5.0

END

相关推荐
程序员的必经之路! 【限时优惠】 现在下单,还享四重好礼: 1、教学课件免费下载 2、课程案例代码免费下载 3、专属VIP学员群免费答疑 4、下单还送800元编程大礼包 【超实用课程内容】  根据《2019-2020年中国开发者调查报告》显示,超83%的开发者都在使用MySQL数据库。使用量大同时,掌握MySQL早已是运维、DBA的必备技能,甚至部分IT开发岗位也要求对数据库使用和原理有深入的了解和掌握。 学习编程,你可能会犹豫选择 C++ 还是 Java;入门数据科学,你可能会纠结于选择 Python 还是 R;但无论如何, MySQL 都是 IT 从业人员不可或缺的技能!   套餐中一共包含2门MySQL数据库必学的核心课程(共98课时)   课程1:《MySQL数据库从入门到实战应用》   课程2:《高性能MySQL实战课》   【哪些人适合学习这门课程?】  1)平时只接触了语言基础,并未学习任何数据库知识的人;  2)对MySQL掌握程度薄弱的人,课程可以让你更好发挥MySQL最佳性能; 3)想修炼更好的MySQL内功,工作中遇到高并发场景可以游刃有余; 4)被面试官打破沙锅问到底的问题问到怀疑人生的应聘者。 【课程主要讲哪些内容?】 课程一:《MySQL数据库从入门到实战应用》 主要从基础篇,SQL语言篇、MySQL进阶篇三个角度展开讲解,帮助大家更加高效的管理MySQL数据库。 课程二:《高性能MySQL实战课》主要从高可用篇、MySQL8.0新特性篇,性能优化篇,面试篇四个角度展开讲解,帮助大家发挥MySQL的最佳性能的优化方法,掌握如何处理海量业务数据和高并发请求 【你能收获到什么?】  1.基础再提高,针对MySQL核心知识点学透,用对; 2.能力再提高,日常工作中的代码换新貌,不怕问题; 3.面试再加分,巴不得面试官打破沙锅问到底,竞争力MAX。 【课程如何观看?】  1、登录CSDN学院 APP 在我的课程中进行学习; 2、移动端:CSDN 学院APP(注意不是CSDN APP哦)  本课程为录播课,课程永久有效观看时长 【资料开放】 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化。  下载方式:电脑登录课程观看页面,点击右侧课件,可进行课程资料的打包下载。
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页