赞
踩
以下根据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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。