赞
踩
以下根据strongswan代码中的testing/tests/route-based/rw-shared-vti/中的测试环境,来看一下基于路由和VTI接口的安全连接。拓扑结构如下:
拓扑图中使用到的设备包括:虚拟主机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网段。字段mark_in和mark_out等于42,其中mark_in指定在inbound方向的policies/SA所使用的Netfilter标记mark值;而mark_out指定在outbound方向上policies/SA设置的Netfilter标记mark值。此处未指定掩码值,即使用默认的0xffffffff。
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 mark_in = 42 mark_out = 42 esp_proposals = aes128gcm128-x25519 } } version = 2 proposals = aes128-sha256-x25519 } } pools { rw_pool { addrs = 10.3.0.0/28 } }
moon网关的配置文件:/etc/strongswan.conf,内容如下。其中install_routes等于0,表明不为IPSec连接创建路由。
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default updown
}
charon {
install_routes = 0
}
操作流程如下,在两个虚拟主机carol和dave上,以及网关moon上启动strongswan进程。再者,在carol和dave上创建名称为home的子连接。前提是在moon网关上创建名称为vti0的vti虚拟接口,其key指定为42,意味着input和output方向的key值(ikey和okey)都指定为42。
moon::ip tunnel add vti0 local PH_IP_MOON remote 0.0.0.0 mode vti key 42
moon::sysctl -w net.ipv4.conf.vti0.disable_policy=1
moon::ip link set vti0 up
moon::ip route add 10.3.0.0/28 dev vti0
moon::iptables -A FORWARD -i vti0 -j ACCEPT
moon::iptables -A FORWARD -o vti0 -j ACCEPT
以下在moon网关上使用ip tunnel命令查看创建的vti0接口,由于在IPSec连接建立之后,发送了两个ping报文,产生了统计信息:
moon:~# ip -s -d tunnel list
vti0: ip/ip remote any local 192.168.0.1 ttl inherit key 42
RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts
2 168 0 0 0 0
TX: Packets Bytes Errors DeadLoop NoRoute NoBufs
2 168 0 0 0 0
moon:~#
内核函数vti_newlink负责创建vti虚拟设备,由于vti属于内核IP隧道的一种,最终使用核心函数ip_tunnel_newlink完成创建。内核使用ip_tunnel结构来表示一个vti设备,其成员parms(ip_tunnel_parm结构)用于保存配置参数,包括以上命令行配置的本地/远端地址,key值等。最后函数ip_tunnel_add将新建隧道添加到网络命名空间中的链表中。
int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm *p, __u32 fwmark)
{
struct ip_tunnel *nt;
struct net *net = dev_net(dev);
struct ip_tunnel_net *itn;
nt = netdev_priv(dev);
itn = net_generic(net, nt->ip_tnl_net_id);
nt->net = net;
nt->parms = *p;
nt->fwmark = fwmark;
err = register_netdevice(dev);
ip_tunnel_add(itn, nt);
使用ip route命令添加目的网段10.3.0.0/28的报文经由vti0虚拟接口。
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 vti0 scope link
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.1
moon:~#
vti0接口的disable_policy选项的使能,将导致接口相关的路由缓存设置DST_NOPOLICY标志(dst_entry->flags)。在策略检查函数xfrm_policy_check的子函数__xfrm_policy_check2中,可见对于转发的报文(sk为空),如果报文的路由缓存项设置了DST_NOPOLICY标志,将跳过策略检查(不执行函数__xfrm_policy_check)。
static inline int __xfrm_policy_check2(struct sock *sk, int dir, struct sk_buff *skb, unsigned int family, int reverse) { struct net *net = dev_net(skb->dev); int ndir = dir | (reverse ? XFRM_POLICY_MASK + 1 : 0); if (sk && sk->sk_policy[XFRM_POLICY_IN]) return __xfrm_policy_check(sk, ndir, skb, family); return (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) || (skb_dst(skb)->flags & DST_NOPOLICY) || __xfrm_policy_check(sk, ndir, skb, family); } static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family) { return __xfrm_policy_check2(sk, dir, skb, family, 0); }
iptables命令在filter表的FORWARD链中添加了如下的两条规则,允许进出vti0虚拟接口的所有流量。
moon:~# iptables -L -v
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
2 168 ACCEPT all -- vti0 any anywhere anywhere
2 168 ACCEPT all -- any vti0 anywhere anywhere
moon:~#
在IPsec连接建立之后,如果在carol主机上ping主机alice,报文到达moon网关之后,由协议栈的函数xfrm4_rcv_encap或者xfrm4_esp_rcv函数处理,前者处理ESP封装在UDP内部的报文;后者处理ESP协议报文。此环境下没有经过NAT设备,所以使用后者进行处理。其中变量全局链表esp4_handlers链表上注册的xfrm协议,进行处理。
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;
对于VTI虚拟设备,内核注册了vti_esp4_protocol协议结构,其中的handler函数指针设置为:vti_rcv,由其进行接收处理。
static struct xfrm4_protocol vti_esp4_protocol __read_mostly = {
.handler = vti_rcv,
.input_handler = vti_input,
.cb_handler = vti_rcv_cb,
.err_handler = vti4_err,
.priority = 100,
};
函数vti_rcv实际上调用函数vti_input进行处理。
static int vti_rcv(struct sk_buff *skb)
{
XFRM_SPI_SKB_CB(skb)->family = AF_INET;
XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
return vti_input(skb, ip_hdr(skb)->protocol, 0, 0);
}
函数vti_input如下。子函数ip_tunnel_lookup在网络命名空间中查找匹配的隧道,注意其参数,报文源地址iph->saddr匹配隧道的远端地址;而报文目的地址iph->daddr匹配隧道的本地地址,在以上vti0隧道接口的创建中,指定了本地地址为moon网关的eth0接口地址:192.168.0.1,而隧道远端地址指定为:0.0.0.0,任意匹配。此处可匹配vti0隧道。
对于xfrm4_policy_check检查函数,如上所述,由于指定了DST_NOPOLICY标志,将跳过检查。接下来,为skb关联隧道结构,调用通用的输入处理函数xfrm_input。
static int vti_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) { struct ip_tunnel *tunnel; const struct iphdr *iph = ip_hdr(skb); struct net *net = dev_net(skb->dev); struct ip_tunnel_net *itn = net_generic(net, vti_net_id); tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY, iph->saddr, iph->daddr, 0); if (tunnel) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto drop; XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = tunnel; return xfrm_input(skb, nexthdr, spi, encap_type);
在xfrm_input函数中,取出skb的回调结构中保存的隧道的输入key值(i_key),此处为42,使用此值作为参数之一在xfrm状态表中查找匹配的xfrm_state,并将匹配状态结构中的mark值赋予skb->mark。
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) { switch (family) { case AF_INET: if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4) mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4->parms.i_key); break; case AF_INET6: if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6) mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6->parms.i_key); break; } do { x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family); skb->mark = xfrm_smark_get(skb->mark, x); if (xfrm_tunnel_check(skb, x, family)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); goto drop; } .... } while (!err); err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
由以下的函数xfrm_tunnel_check可知,VTI只能与XFRM的隧道模式一同使用。
static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x, unsigned int family) { bool tunnel = false; switch(family) { case AF_INET: if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4) tunnel = true; break; case AF_INET6: if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6) tunnel = true; break; } if (tunnel && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL)) return -EINVAL; return 0; }
接下来,看一下alice主机的ping回复报文,其源地址为其eth0接口的IP地址:10.1.0.10,目的地址为carol获取到的虚拟地址:10.3.0.1。在到达moon网关时,通过路由查找,发现路由目的接口为:vti0,执行其发送函数:vti_tunnel_xmit。
如下,函数xfrm_decode_session用于获取报文中的流信息,填充到flowi结构中。之后,将VTI隧道的输出key值(o_key)赋值给流结构中的标记成员flowi_mark。调用vti_xmit函数。
static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); struct flowi fl; switch (skb->protocol) { case htons(ETH_P_IP): xfrm_decode_session(skb, &fl, AF_INET); memset(IPCB(skb), 0, sizeof(*IPCB(skb))); break; case htons(ETH_P_IPV6): xfrm_decode_session(skb, &fl, AF_INET6); memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); break; default: goto tx_err; } /* override mark with tunnel output key */ fl.flowi_mark = be32_to_cpu(tunnel->parms.o_key); return vti_xmit(skb, dev, &fl);
函数vti_xmit中的xfrm_lookup函数根据流结构fl中的信息查找匹配的安全策略和SA,并关联到路由缓存项中。其中在策略匹配时使用的函数xfrm_policy_match,将检查策略中定义的mark是否与以上在flowi_mark指定的相同(考虑掩码)。
vti_state_check函数确认路由缓存中绑定的安全关联SA中的地址信息是否与隧道中配置的地址信息相符。如果路由缓存中的设备等于隧道设备,说明发生了环路。最后,更新报文skb中的路由缓存,以及更新其中的出口设备,由dst_ouput发送数据包。对于IPv4而言,XFRM路由缓存的出口函数为:xfrm4_output,其负责报文的发送。
static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) { struct ip_tunnel *tunnel = netdev_priv(dev); struct ip_tunnel_parm *parms = &tunnel->parms; struct dst_entry *dst = skb_dst(skb); dst = xfrm_lookup(tunnel->net, dst, fl, NULL, 0); if (!vti_state_check(dst->xfrm, parms->iph.daddr, parms->iph.saddr)) { goto tx_error_icmp; } tdev = dst->dev; if (tdev == dev) { dev->stats.collisions++; goto tx_error; } skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev))); skb_dst_set(skb, dst); skb->dev = skb_dst(skb)->dev; err = dst_output(tunnel->net, skb->sk, skb);
以下为策略匹配函数xfrm_poliicy_match的实现,可见对mark值的判断。
static int xfrm_policy_match(const struct xfrm_policy *pol, const struct flowi *fl, u8 type, u16 family, int dir, u32 if_id)
{
const struct xfrm_selector *sel = &pol->selector;
int ret = -ESRCH;
bool match;
if (pol->family != family ||
pol->if_id != if_id ||
(fl->flowi_mark & pol->mark.m) != pol->mark.v ||
pol->type != type)
return ret;
match = xfrm_selector_match(sel, fl, family);
最后在moon网关上使用swanctl命令,可看到carol和dave的SA子连接信息在in/out两个方向上设置的mark值,mark-in=0x0000002a(42)和。mark-out=0000002a
moon:~# swanctl --list-sas --raw
list-sa event {rw {uniqueid=2 version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org initiator-spi=9b04b389c96d16f5 responder-spi=83ea374a4c173a36 encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 established=20 rekey-time=13493 remote-vips=[10.3.0.2] child-sas {net-2 {name=net uniqueid=2 reqid=2 state=INSTALLED mode=TUNNEL protocol=ESP spi-in=c5b7c196 spi-out=c47cc189
mark-in=0000002a mark-out=0000002a encr-alg=AES_GCM_16 encr-keysize=128 bytes-in=84 packets-in=1 use-in=20 bytes-out=84 packets-out=1 use-out=20 rekey-time=3263 life-time=3940 install-time=20 local-ts=[10.1.0.0/16] remote-ts=[10.3.0.2/32]}}}}
list-sa event {rw {uniqueid=1 version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org initiator-spi=1a69f43457a4e7a3 responder-spi=c996b861d29075e3 encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 established=20 rekey-time=13105 remote-vips=[10.3.0.1] child-sas {net-1 {name=net uniqueid=1 reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP spi-in=c55ebfd9 spi-out=c9c1ee2b
mark-in=0000002a mark-out=0000002a encr-alg=AES_GCM_16 encr-keysize=128 bytes-in=84 packets-in=1 use-in=20 bytes-out=84 packets-out=1 use-out=20 rekey-time=3230 life-time=3940 install-time=20 local-ts=[10.1.0.0/16] remote-ts=[10.3.0.1/32]}}}}
list-sas reply {}
No leaks detected, 1442 suppressed by whitelist
moon:~#
以及两个方向的安全策略中的mark/掩码值: mark 0x2a/0xffffffff。
moon:~# ip -s xfrm policy src 10.1.0.0/16 dst 10.3.0.2/32 uid 0 dir out action allow index 857 priority 375423 ptype main share any flag (0x00000000) mark 0x2a/0xffffffff tmpl src 192.168.0.1 dst 192.168.0.200 proto esp spi 0xc5945c6f(3314834543) reqid 2(0x00000002) mode tunnel level required share any enc-mask ffffffff auth-mask ffffffff comp-mask ffffffff src 10.3.0.2/32 dst 10.1.0.0/16 uid 0 dir in action allow index 840 priority 375423 ptype main share any flag (0x00000000) mark 0x2a/0xffffffff tmpl src 192.168.0.200 dst 192.168.0.1 proto esp spi 0x00000000(0) reqid 2(0x00000002) mode tunnel level required share any enc-mask ffffffff auth-mask ffffffff comp-mask ffffffff src 10.1.0.0/16 dst 10.3.0.1/32 uid 0 dir out action allow index 833 priority 375423 ptype main share any flag (0x00000000) mark 0x2a/0xffffffff tmpl src 192.168.0.1 dst 192.168.0.100 proto esp spi 0xcb34a737(3409225527) reqid 1(0x00000001) mode tunnel level required share any enc-mask ffffffff auth-mask ffffffff comp-mask ffffffff src 10.3.0.1/32 dst 10.1.0.0/16 uid 0 dir in action allow index 816 priority 375423 ptype main share any flag (0x00000000) mark 0x2a/0xffffffff tmpl src 192.168.0.100 dst 192.168.0.1 proto esp spi 0x00000000(0) reqid 1(0x00000001) mode tunnel level required share any enc-mask ffffffff auth-mask ffffffff comp-mask ffffffff
strongswan测试版本: 5.8.1
END
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。