如何配置 SIP 和 NAT

作者:Sean Walberg

考虑到互联网协议语音 (VoIP) 背后的所有技术,您可能会期望它能在每个网络上工作,但不幸的是情况并非如此。网络地址转换 (NAT) 是网络中常用的做法,但它与 VoIP 并不兼容。解决此问题需要了解 NAT、VoIP 和您的 VoIP 设置。本文重点介绍 VoIP 的 SIP 协议和 Asterisk VoIP 软件,但问题和解决方案适用于大多数其他情况。

NAT 用于将多个主机隐藏在一组不同的 IP 地址之后。当数据包离开网关时,源地址将被重写为新的外部地址。当返回数据包到达时,网关会将目标地址替换为原始主机的地址,然后再将数据包发送出去。网关还可以为多个内部主机使用相同的外部地址。可以更改连接的源端口,以确保网关可以唯一地标识连接,以便返回的数据包可以替换为正确的地址。

最后一种情况也称为端口地址转换 (PAT) 或 IP 伪装。这是 Linux 和其他家用防火墙用来将多个内部主机隐藏在运营商分配的单个公共 IP 地址后面的方法。主机必须被隐藏,因为它们正在使用特殊的私有地址,这些地址无法在互联网上路由(例如 192.168.1.1)。NAT 通过在所有外部连接上为主机提供正确的源地址来解决连接问题,这允许远程主机响应。缺点是内部必须发起所有连接,以便构建 NAT 工作所需的转换表条目。

VoIP 和 NAT 的问题在于,对话的两端都必须能够相互发起连接。考虑当 PhoneA 使用其各自的 SIP 服务器 PBXA 和 PBXB 呼叫 PhoneB 时发生的简化事件序列。

  1. PBXA 代表 PhoneA 向 PBXB 发送 SIP 邀请。在此邀请中,它是 PhoneA 的 IP 地址。

  2. PBXB 邀请 PhoneB 加入对话,指定 PhoneA 的 IP 地址作为另一端。

  3. 如果 PhoneB 接受呼叫,PBXB 将向 PBXA 响应确认,其中包含 PhoneB 的 IP 地址。

  4. PBXA 告诉 PhoneA 关于 PhoneB 的信息。

  5. PhoneA 使用实时协议 (RTP) 向 PhoneB 发送音频。

  6. PhoneB 使用 RTP 向 PhoneA 发送音频。

NAT 可能会在多个地方引起问题。如果其中一个 PBX 位于 NAT 网关之后,则没有额外的网络设置,另一个 PBX 将无法联系它。如果一个或多个电话位于 NAT 网关之后,则另一个电话将尝试将音频发送到不可路由的地址。这会导致呼叫失败或音频丢失。

Asterisk 将上述步骤 2 和 4 中的电话呼叫切换称为重新邀请或本机桥接。上面的步骤表明,电话与本地 PBX 通话,而本地 PBX 又与远程 PBX 通话。然后,本地 PBX 将呼叫的两端重新指向彼此,以便本地电话与远程端通话。理想情况下,双方都会这样做,并且电话可以自由地直接通话,而无需 SIP 服务器参与对话。

重新邀请的替代方案是让 PBX 在两个端点之间中继语音数据包。我们稍后会更详细地介绍这一点,但首先,这里有一个更常见的场景。

最简单的情况是当 SIP 客户端位于连接到互联网上服务器的 NAT 网关之后时。客户端在首次注册时为 SIP 流量创建转换条目。只要两个主机之间有频繁的通信,例如每分钟一个数据包,通道就会保持打开状态。唯一需要的配置是让客户端在所有 SDP 数据包中使用其外部地址。在支持它的客户端上,启用 STUN(通过 NAT 的 UDP 简单穿越),以便客户端可以动态确定外部地址,或手动输入。Asterisk 目前不支持 STUN,因此所有 NAT 配置都必须手动完成。/etc/asterisk/sip.conf 中的以下命令可以正确设置 NAT

[general]
localnet=192.168.0.0/255.255.0.0 ; or your subnet
externip=x.x.x.x               ; use your address

[YOURREMOTEPEER]               ; your peer's name
nat=yes
qualify=yes                    ; Force keepalives

通过此配置,Asterisk 将对所有配置了 nat=yes 的对等方呼叫使用 externip 定义的地址。添加 qualify=yes 会导致 Asterisk 频繁测试连接,以便 nat 转换不会从防火墙中删除。通过这两个命令,Asterisk 和对等方之间始终会有一个通信通道,并且 Asterisk 在发送 SDP 消息时将使用外部地址。

如果您有多个电话和一个位于 NAT 网关之后的 Asterisk 服务器,则解决方案会变得更加复杂。电话之间的呼叫可以正常工作,因为不需要 NAT。但是,对于您与互联网上的其他系统之间的呼叫,将会出现问题。除非您像上一个示例中那样以客户端身份注册到远程端,否则您将无法接收 SIP 消息,因此您将无法接听电话。其次,呼叫设置中的地址信息将指向电话的内部地址,并且前面提到单向音频问题将会出现。

最简单的解决方案是完全避免 NAT。这在处理 NAT 的文章中可能显得格格不入,但如果您有可用于呼叫服务器的公共 IP 地址,请使用它!如果您的 Asterisk 服务器同时连接到互联网和内部网络,则 SIP 端口可以从内部和外部访问,唯一的问题是确保 RTP 正常流动。PBX 服务器无需配置为在接口之间路由或提供伪装;它只需要桥接入站和出站语音呼叫。

正如我之前提到的,PBX 可以留在语音路径中,也可以退出。在后一种情况下,PBX 会在告诉两个端点关于彼此的信息后,让端点直接通话。但是,Asterisk 可以建立与两个端点的呼叫设置,并代表每个端点中继 RTP 数据包。内部主机将与内部地址通话,而外部主机将与外部地址通话。在 sip.conf 中实现此目的唯一需要的配置是禁用重新邀请

[general]
canreinvite=no     ; force relaying

此配置效果良好,因为 Asterisk 服务器可以自由地与互联网通信以发送和接收呼叫。它还可以与内部电话通话,并且通过一些简单的桥接,完全忽略 NAT。

事实证明,当 Asterisk 服务器只有一个私有地址时,也需要这种中继行为。RTP 端口也必须在防火墙上转发。RTP 根据配置的限制选择随机端口号。在配置端口之前,应限制端口范围。如果端口范围事先已知,则配置防火墙规则要容易得多。

用于 RTP 的端口范围在 rtp.conf 中定义。以下配置将 Asterisk 的 RTP 端口选择限制在 10000 到 10100 之间

[general]
rtpstart=10000 ; first port to use
rtpend=10100   ; last port to use
               ; rounded up if odd

Asterisk 将需要多个 RTP 端口才能正常运行。实际上只使用偶数端口,并且禁用重新邀请会导致每个呼叫建立两个连接。然后必须由防火墙转发这些端口和 SIP 端口。iptables 语法是

iptables -t nat -A PREROUTING -i eth0 -p udp \
-m udp --dport 10000:10100 -j DNAT \
--to-destination 192.168.1.10
iptables -t nat -A PREROUTING -i eth0 -p udp \
-m udp --dport 5060 -j DNAT \
--to-destination 192.168.1.10

将 eth0 替换为防火墙的外部接口,并将 192.168.1.10 替换为您的 Asterisk 服务器的地址。这些规则告诉 Linux 内核转换给定范围内进入外部接口的任何 UDP 数据包的目标地址。这必须在 PREROUTING 阶段而不是 POSTROUTING 阶段发生,因为目标地址正在被转换。此时,来自互联网的任何 SIP 或 RTP 数据包都将转发到内部 Asterisk 服务器进行处理。

当远程站呼叫 Asterisk 时,由于 iptables 规则,SIP 数据包将被转发进来。由于 canreinvite=no 命令,Asterisk 将保留在媒体流中,并且由于 NAT 命令,它将在任何 SDP 数据包中使用防火墙的外部地址。最后,由于 iptables RTP 转发和 rtp.conf 中定义的端口范围的组合,媒体流将被转发到 Asterisk 服务器。

到目前为止,配置的重点是让 Asterisk 在 NAT 网关之后工作,并提供一些额外的细节,以使电话通过 Asterisk 中继。当然,还有更通用的解决方案。

正如我之前所说,如果可以首先避免 NAT,那么这样做符合您的最佳利益,因为它避免了迄今为止遇到的所有问题。可以对 Asterisk 网关应用非常严格的防火墙策略——所需要的只是允许 UDP 5060 用于 SIP 以及 rtp.conf 中定义的任何端口范围。在此配置中,Asterisk 可以联系内部电话和互联网的其余部分。

NAT 问题的最有希望的解决方案是让防火墙在转换源地址时重写 SIP 正文。RTP 会话指定的地址将由防火墙本身替换,防火墙还将负责转发 RTP 流(一旦到达)。一些商业防火墙会这样做。自内核版本 2.6.18 以来,Linux iptables 附带了 ip_nat_sip 和 ip_conntrack_sip 模块。这些模块旨在处理 SIP 转换,但在经过广泛的测试后,我无法使其完全正常工作。我从禁用重新邀请的 VoIP 提供商处成功进行了入站呼叫,但仅此而已。

IP 或 GRE 隧道(未加密)和 IPSec VPN(已加密)是绕过 NAT 需求的另一种选择。多个站点通过隧道连接,隧道将内部流量封装在另一个 IP 数据包中,并在离开外部网关时使用网关的地址。封装在目标端被移除。仅当您事先设置隧道时,此方法才有用。由于 VPN 也用于连接分支机构数据网络,因此您可能已经可以使用此选项。困扰数据应用程序的碎片问题对于 VoIP 来说不是问题,因为使用的是小数据包。

如果 SIP 不是必需的,并且您正在使用 Asterisk,请考虑使用 IAX 协议。IAX 通过单个 UDP 对话隧道传输控制流量和语音流量,可以轻松地对该对话进行端口转发、过滤或转换。此方法仅限于静态隧道集,如果您要通过互联网连接一些 PBX 或连接到长途运营商,则此方法就足够了。

有时,上述解决方案对您不可用。在这种情况下,建议迁移到功能齐全的 SIP 代理,并将 Asterisk 仅用于语音应用程序,例如语音邮件。SIP Express Router (SER) 是一款功能强大的 SIP 服务器,可以很好地处理 NAT,并被包括 Free World Dialup 在内的多项大容量服务使用。SER 的工作仅在于在端点之间建立呼叫,因此它必须依赖其他应用程序(例如专用媒体代理)来处理 RTP 流(如果需要)。

超越 SIP 代理的下一步是会话边界控制器 (SBC),它就像一个 VoIP 防火墙。SBC 可以介入信令或 RTP 路径,以添加额外的功能,例如信令协议或编解码器转换,同时强制执行安全策略。这些几乎都是商业产品。

推出 VoIP 时,尤其是在涉及到互联网和 NAT 时,会遇到问题,这已不是什么秘密。了解所涉及协议的工作原理是解决这些问题的第一步。您现在已经看到了一些解决方案,从重新配置电话或 PBX 到端口转换,再到其他产品,甚至是对架构的调整。解决了这些问题后,您就可以自由享受 VoIP 的好处了。

Sean Walberg 是加拿大温尼伯的一名网络工程师,并且从事 VoIP 工作多年。您可以在 ertw.com 访问他。

加载 Disqus 评论