链路能通过认证,客户端也报告已连接,但一秒后日志便以 LCP 被对端终止收尾。这行信息来自 GRE 隧道内部的 PPP 协商,说明传输本身已经工作,只是两端对链路选项的看法不一致。密码和服务器地址通常没有责任。首先要比较的是加密策略,而在 Linux 上第一个该看的地方是 pppd 的 peer 配置文件。

LCP 失败说明了 PPP 阶段的什么

PPTP 把 PPP 封装在 GRE 里:TCP 1723 控制连接负责建立呼叫,GRE 承载负载,随后 LCP 协商认证协议、压缩和 MPPE 等选项。当某个必需选项在对方那里不可接受时,对端就会终止 LCP。如果日志显示 CHAP 或 MSCHAPv2 交换已经完成、紧接着 LCP 就结束,那属于策略拒绝,而不是可达性故障。如果隧道压根没进入协商阶段,先检查 pppd 及其插件,再去看那行 LCP 记录。

有两个前提很容易被忽略。pppd 可执行文件必须存在,peer 配置文件中指定的 PPTP 插件也必须已安装;缺少软件包会让拨号在任何选项被讨论之前就结束。

在要求加密的一端启用 MPPE

真正的决定在于加密策略由哪一端掌握。客户端可以提议 MPPE、要求它或拒绝它,服务器也做同样的选择。两端都要求时链路可以建立;一方要求而另一方拒绝时,LCP 就会结束。

pty "pptp vpn.example.net --nolaunchpppd"
name alice
remotename PPTP
require-mppe-128

拨号前先用 modprobe ppp_mppe 加载模块,因为没有它 require-mppe-128 会失败。在 Windows 上,对应的控制项是 VPN 条目的安全选项卡,必须让客户端要求加密,而不是接受服务器给出的任何设置。如果服务器仍然终止 LCP,就向其管理员询问 MPPE 是强制、可选还是已禁用,因为客户端无法协商服务器拒绝的保护。为了让这条消息消失而关掉加密是错误的做法:那会移除该协议仅有的保护。

编辑 peer 配置文件之前,先保存能用的版本。把 require-mppe 这行拼错,会重现同样的笼统 LCP 失败,让你回到起点。

改看 pppd 自己的日志,而不是桌面小程序

桌面小程序只显示连接已断开,却把原因藏起来,所以要读守护进程对一次尝试的记录。在使用 systemd 的系统上,消息会进入 journal;更旧的配置则写入传统日志文件。

journalctl --since "10 min ago" | grep -i -E "pppd|lcp|mppe"
tail -n 50 /var/log/syslog

只对单次拨号前后的时间窗做检索,而不是翻一整天的重连记录,这样被拒绝的选项才容易发现。pppd 通常会指出选项名和对端的答复,据此可以判断该修改自己的提议,还是请服务器端放宽要求。让每次尝试都带上时间戳,因为只有知道哪些行属于哪次呼叫时,比较两份日志才有意义。如果完全没有出现 LCP 交换,故障就在链路的更早环节,MPPE 不是嫌疑对象。

让修复在重连后保持:钩子文件名与 MTU

链路稳定后,还有两个细节决定它能否经得起重连。钩子放在 /etc/ppp/ip-up.d 和 /etc/network/if-up.d 中,这两个目录都由 run-parts 处理,而它的文件名过滤器会拒绝任何含点的名字。因此名为 set-pptp.sh 的脚本会被跳过,而 set-pptp 会执行。先核对文件名,再去调试脚本内容,然后在该钩子内部设置更小的 MTU,让每个新建的 ppp 接口无需手工命令就能继承它。

run-parts --test /etc/ppp/ip-up.d
ip link set dev ppp0 mtu 1200

要有意结束会话,应针对 pppd 记录的 pid,而不是按进程名作模式匹配,因为杀掉所有名字含 ppp 的进程也会拆掉无关链路。PPTP 已废弃,MS-CHAPv2 也很弱,所以应把这些检查当作维护一条必须继续工作的链路,而不是当作在任何新地方部署 PPTP 的理由。

相关文章