环境:Linux CentOS 4.5.3-1.el7.elrepo.x86_64
VMWare虚拟机VM1--VM2,两个口直连。VM1:192.168.233.180;VM2:192.168.233.190.
正常配置一个非NAT穿越报文封装的IPsec隧道,然后使用ping命令测试,没有问题,如下配置:
ip xfrm state add src 192.168.233.180 dst 192.168.233.190 proto esp spi 0x00000301 mode tunnel auth md5 0x96358c90783bbfa3d7b196ceabe0536b enc des3_ede 0xf6ddb555acfd9d77b03ea3843f2653255afe8eb5573965df
ip xfrm state add src 192.168.233.190 dst 192.168.233.180 proto esp spi 0x00000302 mode tunnel auth md5 0x99358c90783bbfa3d7b196ceabe0536b enc des3_ede 0xffddb555acfd9d77b03ea3843f2653255afe8eb5573965df
ip xfrm state get src 192.168.233.180 dst 192.168.233.190 proto esp spi 0x00000301
ip xfrm policy add src 192.168.233.180 dst 192.168.233.190 dir out ptype main tmpl src 192.168.233.180 dst 192.168.233.190 proto esp mode tunnel
ip xfrm policy add src 192.168.233.190 dst 192.168.233.180 dir in ptype main tmpl src 192.168.233.190 dst 192.168.233.180 proto esp mode tunnel
ip xfrm policy ls
ip xfrm state add src 192.168.233.180 dst 192.168.233.190 proto esp spi 0x00000301 mode tunnel auth md5 0x96358c90783bbfa3d7b196ceabe0536b enc des3_ede 0xf6ddb555acfd9d77b03ea3843f2653255afe8eb5573965df
ip xfrm state add src 192.168.233.190 dst 192.168.233.180 proto esp spi 0x00000302 mode tunnel auth md5 0x99358c90783bbfa3d7b196ceabe0536b enc des3_ede 0xffddb555acfd9d77b03ea3843f2653255afe8eb5573965df
ip xfrm state get src 192.168.233.180 dst 192.168.233.190 proto esp spi 0x00000301
ip xfrm policy add src 192.168.233.180 dst 192.168.233.190 dir in ptype main tmpl src 192.168.233.180 dst 192.168.233.190 proto esp mode tunnel
ip xfrm policy add src 192.168.233.190 dst 192.168.233.180 dir out ptype main tmpl src 192.168.233.190 dst 192.168.233.180 proto esp mode tunnel
ip xfrm policy ls
但是当使用如下带UDP封装的情况下,死活ping不通。
ip xfrm state add src 192.168.233.180 dst 192.168.233.190 proto esp spi 0x00000301 mode tunnel auth sha1 0x96358c90783bbfa3d7b196ceabe0536b enc aes 0xf6ddb555acfd9d77b03ea3843f2653255afe8eb5573965df encap espinudp 4500 4500 0.0.0.0
ip xfrm state add src 192.168.233.190 dst 192.168.233.180 proto esp spi 0x00000302 mode tunnel auth sha1 0x99358c90783bbfa3d7b196ceabe0536b enc aes 0xffddb555acfd9d77b03ea3843f2653255afe8eb5573965df encap espinudp 4500 4500 0.0.0.0
ip xfrm policy add src 192.168.233.180 dst 192.168.233.190 dir out ptype main tmpl src 192.168.233.180 dst 192.168.233.190 proto esp mode tunnel
ip xfrm policy add src 192.168.233.190 dst 192.168.233.180 dir in ptype main tmpl src 192.168.233.190 dst 192.168.233.180 proto esp mode tunnel
ip xfrm state add src 192.168.233.180 dst 192.168.233.190 proto esp spi 0x00000301 mode tunnel auth sha1 0x96358c90783bbfa3d7b196ceabe0536b enc aes 0xf6ddb555acfd9d77b03ea3843f2653255afe8eb5573965df encap espinudp 4500 4500 0.0.0.0
ip xfrm state add src 192.168.233.190 dst 192.168.233.180 proto esp spi 0x00000302 mode tunnel auth sha1 0x99358c90783bbfa3d7b196ceabe0536b enc aes 0xffddb555acfd9d77b03ea3843f2653255afe8eb5573965df encap espinudp 4500 4500 0.0.0.0
ip xfrm policy add src 192.168.233.180 dst 192.168.233.190 dir in ptype main tmpl src 192.168.233.180 dst 192.168.233.190 proto esp mode tunnel
ip xfrm policy add src 192.168.233.190 dst 192.168.233.180 dir out ptype main tmpl src 192.168.233.190 dst 192.168.233.180 proto esp mode tunnel
通过tcpdump可以看到报文发出了,是ipsec-nat的报文,但是VM2没有任何反应,ip xfrm monitor也抓不到任何信息。
看/proc/net/xfrm_state可以看到XfrmInTmplMismatch一直在增长。
查看说明,应该是tmpl的错误,但是比较内容,以及修改各种参数,一直不行。期间尝试了各种方法,都是不行。
后来网上搜到如下链接:关键字debug xfrm
里面有说
NewsNow: Simple UDP ESP Encapsulation (NAT-T) for AWS EC2 IPSEC Tunnel It appears that the Linux kernel will not decapsulate the packets without some instruction. We need to bind to a socket on the incoming udp port and enable udp encapsulation. Above link has the perl script to do it. After this, my traffic is working without any issues. |
按照里面的方法,编写了提到的perl脚本去创建对应的socket。发现设置IPSEC_POLICY失败。由于对perl不太
熟悉,替换为c语言编写。发现对应的setsockopt返回如下错误:
Operation not supported
按照里面所说,其解决方法也是按照strongswan的提示来的。于是就gdb跟了一下,发现strongswan是如下调用:
socket_default_socket.c : open_socket
kernel_interface.c : bypass_socket
kernel_netlink_ipsec.c : add_socket_bypass
在这里发现使用的是xfrm的参数,于是把c文件改为使用xfrm的。具体c代码和简单makefile如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <net/if.h>
#include <linux/xfrm.h>
int set_ipsec_policy(int fd)
{
struct xfrm_userpolicy_info policy;
u_int sol, ipsec_policy;
sol = IPPROTO_IP;
ipsec_policy = IP_XFRM_POLICY;
memset(&policy, 0, sizeof(policy));
policy.action = XFRM_POLICY_ALLOW;
policy.sel.family = AF_INET;
policy.dir = XFRM_POLICY_OUT;
if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
{
printf("unable to set IPSEC_POLICY on socket: %s\n",strerror(errno));
return -1;
}
policy.dir = XFRM_POLICY_IN;
if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
{
printf("unable to set IPSEC_POLICY on socket: %s\n",strerror(errno));
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int sockfd = -1;
struct sockaddr_in host_addr;
if((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))<0)
{
printf("socket() error!\n");
exit(1);
}
memset(&host_addr, 0, sizeof(host_addr));
host_addr.sin_family = AF_INET;
host_addr.sin_port = htons(4500);
host_addr.sin_addr.s_addr = htonl(INADDR_ANY);
const int on = 1;
if(setsockopt(sockfd, IPPROTO_IP, SO_REUSEADDR, &on, sizeof(on))<0)
{
printf("setsockopt() error!\n");
exit(0);
}
int encap = 2;
if(setsockopt(sockfd, IPPROTO_UDP, 100, &encap, sizeof(encap))<0)
{
printf("setsockopt() udp error!\n");
exit(0);
}
if (bind(sockfd, (struct sockaddr*)&host_addr, sizeof(host_addr)) < 0)
{
printf("unable to bind socket: %s\n", strerror(errno));
close(sockfd);
return -1;
}
set_ipsec_policy(sockfd);
printf("bind ..\n");
while(1)
{
sleep(1);
}
return 0;
}
all:
gcc -o open_nat_t_port -g open_nat_t_port.c
clean:
rm open_nat_t_port -f
两边都运行此程序,ping可以通了,通过抓包发现,确实也是ipsec-nat包。