基于路由和XFRM虚拟接口的IPSec实现

以下根据strongswan代码中的testing/tests/route-based/rw-shared-xfrmi/中的测试环境,来看一下基于路由和XFRM接口的安全连接。拓扑结构如下:
在这里插入图片描述

拓扑图中使用到的设备包括:虚拟主机carol和dave,以及虚拟网关moon。

虚拟主机配置

carol的配置文件:/etc/swanctl/swanctl.conf,内容如下。连接home中的字段vips设置为0.0.0.0,标识carol并不请求特定的虚拟IP地址。

connections {

   home {
      local_addrs  = PH_IP_CAROL
      remote_addrs = PH_IP_MOON
      vips = 0.0.0.0

      local {
         auth = pubkey
         certs = carolCert.pem
         id = carol@strongswan.org
      }
      remote {
         auth = pubkey
         id = moon.strongswan.org
      }
      children {
         home {
            remote_ts = 10.1.0.0/16

            updown = /usr/local/libexec/ipsec/_updown iptables
            esp_proposals = aes128gcm128-x25519
         }
      }
      version = 2
      proposals = aes128-sha256-x25519
   }
}

dave主机配置的配置与以上carol主机类似。

网关配置

moon网关的配置文件:/etc/swanctl/swanctl.conf,内容如下。其中为远程用户定义了虚拟地址池rw_pool,地址为10.3.0.0/28网段。字段if_id_out和if_id_in等于42,其中if_id_in指定在inbound方向的policies/SA所使用的xfrm虚拟接口的ID;而if_id_out指定在outbound方向上policies/SA设置的XFRM虚拟接口的ID值。对于设置了if_id_out值的子连接SA,strongswan进程不在配置SA相关的路由信息,而是通过XFRM虚拟接口完成路由。

connections {

   rw {
      local_addrs  = PH_IP_MOON
      pools = rw_pool

      local {
         auth = pubkey
         certs = moonCert.pem
         id = moon.strongswan.org
      }
      remote {
         auth = pubkey
      }
      children {
         net {
            local_ts  = 10.1.0.0/16

            if_id_out = 42
            if_id_in = 42

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

pools {
    rw_pool {
        addrs = 10.3.0.0/28
    }
}

连接建立流程

操作流程如下,首先在两个虚拟主机carol和dave上,以及网关moon上启动strongswan进程。再者,在carol和dave上创建名称为home的子连接。

moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon -i 42 -d eth0
moon::ip link set xfrm-moon up
moon::ip route add 10.3.0.0/28 dev xfrm-moon
moon::iptables -A FORWARD -i xfrm-moon -j ACCEPT
moon::iptables -A FORWARD -o xfrm-moon -j ACCEPT
...

以上的xfrmi命令将在moon网关上创建虚拟的XFRM接口:xfrm-moon,并且指定其ID值(xfrm_id)为42,其底层物理接口为eth0。可使用xfrmi -list命令进行查看,strongswan中的文件:src/xfrmi/xfrmi.c实现了此xfrmi工具,其使用netlink套接口(NETLINK_ROUTE)向内核发送创建/显示等命令。

moon:~# 
moon:~# /usr/local/libexec/ipsec/xfrmi -list
13: xfrm-moon        dev eth0     if_id 0x0000002a [42]
No leaks detected, 2 suppressed by whitelist
moon:~# 
moon:~# 
moon:~# ip -d link show dev xfrm-moon
13: xfrm-moon@eth0: <NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/none 52:54:00:c7:b8:b0 brd ff:ff:ff:ff:ff:ff promiscuity 0 
    xfrm addrgenmode random numtxqueues 1 gso_max_size 65536 gso_max_segs 65535 
moon:~# 

使用ip route命令查看为xfrm-moon虚拟接口添加的路由信息,网段10.3.0.0/28的流量路由到xfrm-moon接口。

moon:~# 
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.3.0.0/28 dev xfrm-moon scope link 
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.1 
moon:~# 

以下配置的iptables规则,允许进出xfrm-moon接口的所有流量。

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

内核文件:net/xfrm/xfrm_interface.c负责处理XFRM虚拟接口,内核使用xfrm_if结构表示一个XFRM虚拟接口,其成员phydev为底层的物理接口,例如以上的eth0。成员p的结构类型为xfrm_if_parms,其中if_id保存了命令行中指定的ID值(xfrm_id),例如以上的42。

所有XFRM虚拟接口链接在网络命名空间的xfrmi_net链表中。

static struct xfrm_if *xfrmi_create(struct net *net, struct xfrm_if_parms *p)
{
    struct net_device *dev;
    struct xfrm_if *xi;

    dev = alloc_netdev(sizeof(*xi), name, NET_NAME_UNKNOWN, xfrmi_dev_setup);

    xi = netdev_priv(dev);
    xi->p = *p;
    xi->net = net;
    xi->dev = dev;
    xi->phydev = dev_get_by_index(net, p->link);

    err = xfrmi_create2(dev);

在IPsec连接建立之后,如果在carol主机上ping主机alice,报文到达moon网关之后,由协议栈的函数xfrm4_rcv_encap或者xfrm4_esp_rcv函数处理,前者处理ESP封装在UDP内部的报文。

int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
    struct xfrm4_protocol *handler;
    struct xfrm4_protocol __rcu **head = proto_handlers(nexthdr);

    XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
    XFRM_SPI_SKB_CB(skb)->family = AF_INET;
    XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);

    for_each_protocol_rcu(*head, handler)
        if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL)
            return ret;

static int xfrm4_esp_rcv(struct sk_buff *skb)
{
    struct xfrm4_protocol *handler;

    XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;

    for_each_protocol_rcu(esp4_handlers, handler)
        if ((ret = handler->handler(skb)) != -EINVAL)
            return ret;

二者最终都会调用到xfrm-moon类型虚拟接口注册的协议处理函数,如下xfrmi_esp4_protocol结构定义中的函数xfrm_input和xfrm4_rcv。这两个函数最终都是对xfrm_input函数的调用。

static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = {
    .handler    =   xfrm4_rcv,
    .input_handler  =   xfrm_input,
    .cb_handler =   xfrmi_rcv_cb,
    .err_handler    =   xfrmi4_err,
    .priority   =   10,
};

函数xfrm_input中和xfrm-moon虚拟接口相关的调用部分如下,对于IPv4而言,最后将调用到xfrm4_protocol结构中的cb_handler回调函数,即xfrm虚拟接口的:xfrmi_rcv_cb函数。

int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
    err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
    if (err)
        goto drop;
		
static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol, int err)
{
    const struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family);

    ret = afinfo->callback(skb, protocol, err);

int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
{
    struct xfrm4_protocol *handler;
    struct xfrm4_protocol __rcu **head = proto_handlers(protocol);

    for_each_protocol_rcu(*head, handler)
        if ((ret = handler->cb_handler(skb, err)) <= 0)
            return ret;

如下为xfrmi_rcv_cb函数,其中子函数xfrmi_lookup将xfrm状态结构中的的if_id,与保存在网络命名空间中的xfrmi虚拟接口结构中的if_id做比较,查抄对应的xfrm虚拟接口。之后,将xfrm虚拟接口设备设置为报文skb的入口设备。

static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
{
    struct xfrm_mode *inner_mode;
    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;

接下来看一下alice主机的ping回复报文,其源地址为其eth0接口的IP地址:10.1.0.10,目的地址为carol获取到的虚拟地址:10.3.0.1。在到达moon网关时,通过路由查找,发现路由目的接口为:xfrm-moon,执行其发送函数:xfrmi_xmit。

xfrm_decode_session函数负责获取报文中的流信息,填充到flowi结构中。之后,将流结构的成员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));

    switch (skb->protocol) {
    case htons(ETH_P_IP):
        xfrm_decode_session(skb, &fl, AF_INET);
        memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
        break;
		...
    }

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

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

第二阶段的发送函数xfrmi_xmit2如下,其中xfrm_lookup_with_ifid函数,将根据流信息fl和if_id(此处为42),查找匹配的xfrm策略,最终将生成xfrm_dst结构的路由信息结构,并绑定xfrm策略和状态state,并且将dst的ouput函数设置为:(xfrm_mode)inner_mode->afinfo->output,对于IPv4协议而言,此处的output函数指针为全局结构xfrm4_state_afinfo的成员函数xfrm4_output。

函数最后,重新设置报文skb的路由缓存,更改报文的出口网络设备为xfrm-moon设备,调用dst_ouput执行发送,即函数xfrm4_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;

    tdev = dst->dev;

    xfrmi_scrub_packet(skb, !net_eq(xi->net, dev_net(dev)));
    skb_dst_set(skb, dst);
    skb->dev = tdev;

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

最后在moon网关上使用swanctl命令,可看到carol和dave的SA子连接信息在in/out两个方向上的xfrm-moon的ID值:0x0000002a(42)。

moon:~# swanctl --list-sas
rw: #30, ESTABLISHED, IKEv2, 8895b4223036f3ec_i ffbb02e8e042786d_r*
  local  'moon.strongswan.org' @ 192.168.0.1[4500]
  remote 'carol@strongswan.org' @ 192.168.0.100[4500] [10.3.0.1]
  AES_CBC-128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/CURVE_25519
  established 4101s ago, rekeying in 9144s
  net: #117, reqid 1, INSTALLED, TUNNEL, ESP:AES_GCM_16-128/CURVE_25519
    installed 1341s ago, rekeying in 1968s, expires in 2619s
    in  c75b4379 (-|0x0000002a),      0 bytes,     0 packets
    out c2297949 (-|0x0000002a),      0 bytes,     0 packets
    local  10.1.0.0/16
    remote 10.3.0.1/32
rw: #29, ESTABLISHED, IKEv2, 9ca519c3686dab44_i* 61569c2afab089e2_r
  local  'moon.strongswan.org' @ 192.168.0.1[4500]
  remote 'dave@strongswan.org' @ 192.168.0.200[4500] [10.3.0.2]
  AES_CBC-128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/CURVE_25519
  established 6716s ago, rekeying in 6622s
  net: #118, reqid 2, INSTALLED, TUNNEL, ESP:AES_GCM_16-128/CURVE_25519
    installed 1301s ago, rekeying in 2081s, expires in 2659s
    in  c946d50d (-|0x0000002a),      0 bytes,     0 packets
    out cfeef9fc (-|0x0000002a),      0 bytes,     0 packets
    local  10.1.0.0/16
    remote 10.3.0.2/32
No leaks detected, 1442 suppressed by whitelist
moon:~# 

strongswan版本: 5.8.1
内核版本: 5.0

END

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页