注册成功几乎说明不了音频的情况。软电话可以与办公室 PBX 保持有效绑定数小时,却仍然只有沉默。隧道只承载有人主动送进去的数据包,而一通电话会走三条独立路径:绑定、对话建立和 RTP 流。把它们分开之后,故障就落在某一条路径上,而不是落在 VPN 这个词上。

设想一台家用 Wi-Fi 上的笔记本电脑,通过一条已批准的隧道拨打办公室分机。PBX 认证了用户,接受了 REGISTER,分机显示为可用,但通话接通后一个方向完全没有声音。

注册绑定的是联系人,而不是媒体路径

REGISTER 请求要求注册服务器保存一条从记录地址到一个或多个联系 URI 的映射。这条映射只回答一个很窄的问题:新的对话现在应该发往哪里。它对媒体应该发往哪里只字不提,注册被接受也不能证明后续的 INVITE 会携带可达的媒体地址。注册还会按计时器过期,因此停止刷新的电话会失去来电,而它的隧道看起来仍然健康。要盯住联系人的过期时间,而不是隧道状态。

当软电话位于隧道之后时,联系人头域可能携带私有地址。如果 PBX 把它转发给另一个端点,就会建立一个指向被叫无法到达的网络的对话。在 PJSIP 端点上设置 rewrite_contact=yes 会让 PBX 用它观察到的源地址进行替换,这往往就是响铃与超时之间的差别。

INVITE 提供什么,以及谁能使用它

带有 SDP 正文的 INVITE 会提出主叫希望在哪里接收媒体:一个地址、一个端口以及一份编解码列表。被叫用自己的地址和端口作答,随后双方各自把 RTP 发往对方提出的位置。如果主叫提出的是只存在于自家网络的 192.168.x.x 地址,那么无论信令多么干净,被叫都没有地方可以发送音频。伪装会改写在离开路由器时的 IP 头,但从不改写 SDP 正文内部的文本。

这个缺口解释了经典的单向通话:信令能工作,是因为两端都能到达 PBX;音频失败,是因为有一端被告知要发往一个只存在于另一个 NAT 之后的地址。

PathWho picks the addressTypical failure
REGISTERSoftphone, rewritten by the registrar if configuredBinding expires or points at a private address
INVITE and SDPEach endpoint in its own offer or answerOffer names a media address nobody can route to
RTPPBX relay policy or the two endpointsPort range blocked, or no return path

由谁承载音频是配置选择

信令和媒体是两种不同的角色。代理可以整通电话都留在信令路径上,而完全不碰任何一个 RTP 包。PBX 是否成为媒体中继取决于配置,而不是隧道是否存在。

在直接媒体模式下,两端自己交换 RTP,这在一个局域网里工作良好,而当一侧位于很窄的端口范围之后时就会很糟。

设置 direct_media=no 会把 PBX 拉入媒体路径,使两条腿都终止在它控制的地址上,用带宽和处理器换取可预测的寻址。中继时 PBX 仍然必须通告一个可达地址。在多宿主主机上,pjsip.conf 中的 external_media_address 和 external_signaling_address 决定 SDP 和 Via 头里出现什么。如果不设置它们,PBX 可能把自己的局域网地址提供给唯一路径是隧道另一端的呼叫者。

在动设置之前先追踪每个方向

通话进行时,先从 PBX 的套接字表看起:ss -uanp | grep -E '5060|10000' 会显示 RTP 端口是否已绑定,以及使用的是哪个本地地址。如果媒体范围内没有任何绑定,说明中继没有启用,两端在直接对话。然后在软电话一侧用 tcpdump -ni any -s0 udp portrange 10000-20000 -w rtp.pcap 抓包。只有出包没有入包,就把问题缩小到回程路径,或缩小到被要求使用的那个地址;抓包为空则说明媒体根本没有到达隧道接口。用 sngrep 抓信令,可以看到实际发出的 SDP 连接行,从而回答另一半问题。

把连接行里的地址,与远端真正能够路由到的地址作比较。仅这一项比较,就能比任何配置审查更快地解决大多数单向音频报告。

加密止于隧道边缘

TLS 上的 SIP 保护它所配置的那一跳信令,SRTP 在两端协商后保护媒体,隧道保护其自身端点之间的流量。这些保护彼此不能替代。在隧道内加密的通话,仍可能在出网关后以明文形式走向 PBX,这是设计决策而不是缺陷。

在关闭工单之前,按顺序确认三件事:注册是新鲜的,SDP 携带远端可达的地址,数据包在媒体端口上双向流动。只要有一项不成立,隧道就不是问题所在。修好失败的那条路径,然后重跑同样的抓包来确认变化。保留改动前后的抓包,下一次单向报告就会变成五分钟的检查,而不是一整个下午。

相关文章