为什么 IKEv2 在 Android 上会因非标准 UDP 端口失败
系统的 IKEv2 表单只接受地址,不接受端口。先确认客户端是否支持自定义端口,再设置服务端两个端口,然后检查封装与日志。
网关在 UDP 4443 上应答 IKEv2 是有原因的,配置文件里有一个名为 Server 的字段,你填入主机名和端口,手机却报告无法访问服务器或无法解析该地址。并没有什么东西被拦截。Android 的系统表单只期待一个主机,Windows 上也是如此。自定义端口首先是客户端的能力,其次才是服务端的设置,而只有部分客户端允许你表达它。按顺序排查三部分:客户端能说什么、服务端在听什么,以及两端是否用同样的方式封装数据包。
手机实际发出的内容
在为本名设计的字段里填入 host:port 字符串,它会被当作域名去解析。解析器对 vpn.example.net:4443 什么也不返回,你看到的错误是解析失败或不可达,而不是端口超时。这个区别很重要,因为换更短的名字或换网络重试,永远修不好表单根本解析不了的字符串。
Android 的系统 IKEv2 配置文件与 Windows 内置 VPN 客户端一样,固定在标准 IKE 端口上。两者都不提供端口字段,改配置文件也加不出来。如果运营方的监听确实在 4443,那么内置客户端不是配置错了,而是用错了工具。
换一个能指定端口的客户端
strongSwan 的 Android 客户端在高级配置文件设置中记录了可配置的服务端端口,所以这套方案几乎每个可用配方里都能看到它。这项支持属于那个客户端:另一个 IKEv2 应用,或同一应用的厂商分支,即使底层 strongSwan 守护进程支持,也未必提供同一字段。
一次性向运营方要齐整套信息:网关名称、UDP 端口、认证方式,以及客户端必须期待的服务器身份。自定义端口不是关闭证书校验或更换协议的理由。把端口填在已安装客户端要求的位置;如果该客户端没有这个字段,答案就是换一个获准的客户端,而不是猜一个绕法。
服务端有两个端口,两个都必须对上
IKE 从 UDP 500 开始,NAT 穿越通常把 IKE 和封装后的 ESP 移到 UDP 4500。这两个在 strongSwan 里是分开的设置,所以把服务端改到单一自定义端口时,两者必须改成同一个值:
plugins {
socket-default {
port = 4443
port_nat_t = 4443
}
}
一个端口同时承担两种角色时,守护进程必须在同一个套接字上区分 IKE 与 UDP 内的 ESP,靠的是 IKE 消息前面那个四字节的非 ESP 标记。所以客户端不可能先在自定义端口上协商、再悄悄继续走 4500;这也是“只改端口”这句话掩盖了一个封装前提的原因。
端口转发不等于监听
把 4443 转发到一个普通 IKE 监听上,是那种看起来几乎成功的失败模式。第一次交换到了,服务器也回了,随后连接停滞或反复重协商到什么都没有,因为回应是从另一个端口发出的,后续封装流量永远对不上两端建立的状态。只改写目的端口却不移动监听,得到的正是这种半开的结果。
路径还必须端到端放行 UDP。TCP 443 能穿过酒店或运营商网络,并不说明 UDP 端口上的 IKEv2 可行,哪怕端口号相同。只放行 TCP 443 的地方,自定义 UDP 端口不会把 IKEv2 变成 HTTPS,正确的答案可能是换一条接入路径,而不是再换端口试一次。
测试时把三个阶段分开
在客户端只尝试一次的同时,在服务端抓包。请求始终不到,是端点或防火墙的问题;请求到了却没有回应,是监听或策略的问题;回应之后陷入沉默,是封装或身份的问题。
tcpdump -ni eth0 -vv udp port 4443
然后去读客户端的报文,而不是看它的转圈图标。提案错误意味着算法不一致,认证失败意味着身份或证书不一致,反复重传却没有响应,则说明那个端口上根本没有东西在应答。一次只测一个阶段,两次尝试之间只改一个字段;strongSwan 关于 NAT 穿越 的文档描述了抓包正在向你展示的标记与端口行为。