环境与现象

  • Windows 客户端连接 IKEv2 时提示 “服务器无响应”
  • VPN 服务器地址:192.168.1.200(飞牛OS Docker 部署)
  • 客户端地址:192.168.1.201,局域网互通正常(ping 0ms)
项目 详情
宿主机 飞牛OS
部署方式 Docker 容器 ipsec-vpn-server
IKE 软件 Libreswan 5.3.1(IKE daemon: pluto
L2TP 软件 xl2tpd 1.3.18
容器权限 --privileged

排查过程

第一步:网络层端口探测

在客户端使用多种工具测试服务器 UDP 500(ISAKMP)和 UDP 4500(NAT-T)端口:

BASH
# ncat 测试
ncat -u -v -z -w 3 192.168.1.200 500
ncat -u -v -z -w 3 192.168.1.200 4500

# Python socket 空探测
python3 -c "
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 0))
sock.sendto(b'', ('192.168.1.200', 500))  # 发空包
# 收到 ICMP port-unreachable → 端口关闭
# 超时无 ICMP 错误 → 端口开放
"
点击展开查看更多

结果:

端口 状态 说明
UDP 500 (ISAKMP) ❌ 关闭 服务器返回 ICMP Port Unreachable
UDP 4500 (NAT-T) ✅ 开放 无 ICMP 错误返回

IKEv2 协商流程是客户端先向 UDP 500 发起 IKE_SA_INIT 请求,500 不通则协商根本无法开始。这就是 Windows 报"服务器无响应"的直接原因。

第二步:定位问题到 IKE Daemon

进入 Docker 容器检查进程和端口监听:

BASH
# 容器内只有 xl2tpd,没有 IKE 进程在监听
$ netstat
(空列表——没有任何端口监听信息)

$ ps aux
root  1   xl2tpd -D                      # L2TP 隧道进程
root  822 /bin/sh /usr/.../ipsec/_plutorun --config /etc/ipsec.conf --nofork
root  823 sleep 10                       # ← 关键线索!
点击展开查看更多

发现关键问题:

_plutorun 是 Libreswan 的 watchdog 脚本。看到 _plutorun + sleep 10 同时存在,说明它进入了一个死循环:尝试启动 pluto → 立即崩溃 → 等 10 秒 → 重试 → 无限循环。

BASH
$ ipsec status
ERROR: ipsec whack: Pluto is not running (no "/run/pluto/pluto.ctl")
点击展开查看更多

Pluto 根本没在运行。

第三步:手动启动 Pluto 抓崩溃原因

BASH
$ /usr/local/libexec/ipsec/pluto --config /etc/ipsec.conf --nofork
Pluto is starting ...
Aborted (core dumped)
点击展开查看更多

Pluto 直接 SIGABRT 崩溃。用 strace 追踪系统调用,定位到崩溃前的最后操作为:

BASH
$ strace -f /usr/local/libexec/ipsec/pluto --config /etc/ipsec.conf --nofork 2>&1 | tail -50
[pid  5625] socket(AF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_XFRM) = -1 EPROTONOSUPPORT (Protocol not supported)
                                                                                            ^^^^^^^^^^^^^^^^
                                                                                            协议不受支持!
点击展开查看更多

NETLINK_XFRM 套接字创建失败,pluto 随即调用 tgkill(getpid(), SIGABRT) 自杀。

第四步:确认宿主机内核状态

NETLINK_XFRM 协议由内核模块 xfrm_user 提供。检查宿主机:

BASH
$ cat /proc/config.gz | gunzip | grep CONFIG_XFRM_USER
CONFIG_XFRM_USER=m        # ← 内核编译为模块,支持!

$ lsmod | grep xfrm
xfrm_interface            # ← 有这个
xfrm6_tunnel              # ← 有这个
tunnel4                   # ← 有这个
tunnel6                   # ← 有这个
# 但是...
# xfrm_user 不在列表中!  ← 模块存在但未加载
点击展开查看更多

结论: 飞牛OS 内核编译了 CONFIG_XFRM_USER=m(模块),但 xfrm_user 模块没有被加载。Pluto 需要这个模块提供 NETLINK_XFRM 用户态接口来管理 IPsec SA 和策略,拿不到就崩溃。

PLAINTEXT
xl2tpd(正常)       → L2TP 用 l2tp_core/l2tp_ppp 模块 ✅
pluto(崩溃)        → IKE 需要 xfrm_user 模块        ❌ 未加载
点击展开查看更多

解决方案

BASH
# 1. 加载 xfrm_user 内核模块
sudo modprobe xfrm_user

# 2. 确认加载成功
lsmod | grep xfrm_user

# 3. 重启 Docker 容器
sudo docker restart ipsec-vpn-server

# 4. 设置开机自动加载,避免重启后失效
echo "xfrm_user" | sudo tee /etc/modules-load.d/ipsec.conf
点击展开查看更多

执行后,pluto 成功启动,UDP 500 和 4500 端口正常监听,Windows 客户端顺利连接。


总结

PLAINTEXT
IKEv2 VPN 依赖链条:

Windows 客户端
    │ UDP 500 (IKE_SA_INIT)
    ▼
pluto (Libreswan IKE daemon)
    │ socket(AF_NETLINK, ..., NETLINK_XFRM)
    ▼
xfrm_user 内核模块         ← 这一步断了
    │
    ▼
Linux 内核 IPsec 栈 (XFRM)
点击展开查看更多
  • l2tp_core / l2tp_ppp → xl2tpd 使用,和 IKE 无关
  • xfrm_interface / xfrm6_tunnel → IPsec 接口/隧道框架,和用户态配置接口无关
  • xfrm_user用户态程序通过 netlink 配置 IPsec 的接口,pluto/charon 启动必须依赖它

这三个是不同东西,模块名相近容易混淆。本次问题就是前两个已加载,唯独缺了真正需要的 xfrm_user

版权声明

作者: Cheyne Xie

链接: https://chaim.eu.org/posts/1e4614ee/

许可证: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键