FreeSWITCH SIP中继注册403:From/To/Contact域名不一致的方案取舍
FreeSWITCH SIP中继注册403:From/To/Contact域名不一致的方案取舍

FreeSWITCH SIP中继注册403:From/To/Contact域名不一致的方案取舍

业务场景

运营商SIP中继注册失败是个高频问题。FreeSWITCH连上运营商的接入点,sofia status profile external 显示 REG_FAIL,运营商返回 403 Forbidden

大多数人的排查路径是:密码对了没?IP白名单加了没?User-Agent要不要改?改了一圈还是403。

抓个包看看:

REGISTER sip:trunk.operator.com SIP/2.0
From: <sip:your_number@your_domain.com>
To: <sip:your_number@your_domain.com>
Contact: <sip:your_number@172.16.1.100:5060>

问题往往不在认证环节——运营商校验的是 SIP 头域里的域名格式,而不是你的密码。From/To 头域里的 @your_domain.com 和 Contact 头域里的 172.16.1.100 不匹配运营商的要求,注册请求在认证之前就被拒绝了。

From/To 头域(SIP身份标识):格式 sip:用户名@域名,运营商用它判断请求者身份是否在授权列表里。

Contact 头域(SIP位置标识):格式 sip:用户名@主机地址:端口,运营商用它决定把呼叫路由到哪里。

技术判断:为什么是域名格式问题

区分「域名格式校验失败」和「认证失败」有个简单方法:看 403 响应的 Reason 头域。

运营商返回的 403 如果带有 Reason: Q.850;cause=88;text="Prefix or Number Unknown"Reason: SIP;cause=403;text="Domain Mismatch" 这类描述,基本可以确定是域名格式问题,不是密码问题。

另一个判断依据:关掉 auth-calls 参数后如果还是 403,说明根本没走到认证流程。认证失败的 403 在关闭认证后至少能通过校验——虽然后续呼叫可能会有别的问题,但不会在注册阶段就被打回。

抓包确认的命令:

tcpdump -i any -n port 5060 -v | grep -E "(From:|To:|Contact:|REGISTER|SIP/2.0 403)"

方案取舍

解决 From/To/Contact 域名不一致的问题,主流方案有三个。我先说结论,再展开解释为什么。

结论:单中继选方案一,多中继选方案二,方案三能不碰就不碰。

方案一:force-register-domain(Profile级强制域名)

在 Sofia Profile 配置里加一行,让所有发出去的 Register 请求使用统一的 From/To 域名:

<param name="force-register-domain" value="operator_trunk_domain.com"/>

这个方案的优点是配置极简,一次生效。改一行配置,所有经过这个 Profile 的注册请求都统一用这个域名,不需要逐个 Gateway 去调。

缺点是只能指定一个域名。如果你只有一个运营商中继,这个方案完美。如果有多个中继、每个要用不同的域名,这个方案就不够用了。

适用场景:对接单一运营商SIP中继,或者所有中继都要求相同的 From/To 域名格式。

方案二:Gateway级独立配置(推荐多中继场景)

在 Gateway 配置里单独设置 register-proxycontact-params

<gateway name="operator_trunk">
  <param name="username" value="your_number"/>
  <param name="realm" value="operator_trunk_domain.com"/>
  <param name="password" value="your_password"/>
  <param name="proxy" value="trunk.operator.com"/>
  <param name="register-proxy" value="trunk.operator.com"/>
  <param name="contact-params" value="domain_name=operator_trunk_domain.com"/>
</gateway>

这个方案的优点是灵活性高,每个 Gateway 独立配置,想加新中继直接新建 Gateway,不影响现有的。缺点是配置项多,需要搞清楚 realmproxyregister-proxy 各自的用途——用错参数的话注册还是失败。

realm:认证 realm,通常与运营商要求的域名一致。

proxy:SIP Proxy 地址,注册请求发往的目标。

register-proxy:注册代理,强制指定注册请求的路由地址。有些运营商要求 proxy 和 register-proxy 设为同一个值。

适用场景:对接多个运营商中继,或者同一个运营商但需要使用不同域名格式。

方案三:accept-blind-reg(风险最高的方案)

<param name="outbound-proxy" value="sip:trunk.operator.com"/>
<param name="accept-blind-reg" value="true"/>

accept-blind-reg 的作用是跳过域名校验,接受任何格式的 Register 请求

为什么不推荐:

  1. 安全风险极高。任何人都能用任意域名向你发起 Register 请求,你的系统来者不拒。
  2. 治标不治本。运营商侧还是会校验你的域名格式,你关掉本地校验不等于运营商放过你。
  3. 增加排查难度。出了问题是本地问题还是运营商问题更难定位。

这个方案只适合在排查阶段临时开启,确认到底是哪个环节出问题。一旦定位到问题,立即关闭。生产环境绝对不能开。

方案取舍的判断逻辑

┌─────────────────────────────────────────────┐
│  你需要对接几个运营商中继?                   │
└─────────────────────────────────────────────┘
                    │
         ┌─────────┴─────────┐
         ▼                   ▼
     只有一个           多个,或者
         │            域名格式不同
         ▼                   ▼
   方案一:force-     方案二:Gateway
   register-domain   级独立配置
         │                   │
         └───┬───────────────┘
             ▼
      验证注册是否成功
             │
      ┌──────┴──────┐
      ▼             ▼
   成功           失败→抓包看Reason描述
                              │
               ┌───────────────┴───────────────┐
               ▼                               ▼
          域名问题               协议问题(UDP/TCP)
          继续调整域名            改 transport 参数

实现步骤

第一步:确认当前配置和注册状态

# 查看 Profile 级别的注册状态
sofia status profile external

# 查看特定 Gateway 的详细状态
sofia status gateway operator_trunk

# 查看当前有哪些已注册的 Contact
sofia status profile external reg

第二步:清除旧注册记录

修改域名配置后,必须清除 FreeSWITCH 缓存的注册信息,否则旧的 Contact 记录会残留,运营商那边的旧注册也不会自动过期。

# 在 fs_cli 里执行,强制清除指定 Gateway 的注册
sofia profile external kill-register operator_trunk

# 或者用 unregister 命令
sofia unregister operator_trunk trunk.operator.com

第三步:修改配置

方案一的配置(在 sip_profiles/external.xml 里):

<!-- 运营商要求 From/To 使用 operator_trunk_domain.com -->
<param name="force-register-domain" value="operator_trunk_domain.com"/>
<param name="force-register-db-domain" value="operator_trunk_domain.com"/>

force-register-db-domain 确保 FreeSWITCH 内部数据库也使用这个域名存储注册信息,避免 Profile 内查询不到刚注册上的 Contact。

方案二的前提:确保 Gateway 配置文件存在,通常在 sip_profiles/gateways/operator_trunk.xml。如果不存在,参考上面方案二的配置内容创建。

第四步:重启 Profile 让配置生效

sofia profile external restart

重启后观察几秒钟,然后用 sofia status gateway operator_trunk 检查状态。

第五步:验证

注册成功的标志是状态显示 REGED

================================================================================
Name                State        Port    Secure  Username    Realm
================================================================================
operator_trunk      REGED        5060    No     your_number  operator_trunk_domain.com
================================================================================

如果还是 REG_FAIL,抓包分析新的 403 响应里的 Reason 描述:

# 捕获 SIP 流量到文件
tcpdump -i any -n host trunk.operator.com and port 5060 -w /tmp/sip_capture.pcap

# 用 sngrep 查看(如果已安装)
sngrep -I /tmp/sip_capture.pcap

# 或者用 tshark 提取关键头域
tshark -r /tmp/sip_capture.pcap -Y "sip" -T fields -e sip.msg_hdr | grep -E "(From|To|Contact|Reason)"

边界条件与风险点

NAT/SBC 场景

如果 FreeSWITCH 前面有 SBC(Session Border Controller)或 NAT 设备,Contact 头域里的地址必须是公网可达的。运营商看到的 Contact 必须能路由回来,否则注册成功也没用——呼入的 INVITE 送不到你的 FreeSWITCH。

这种情况下需要额外配置:

<param name="NDLB-force-rport" value="true"/>
<param name="local-network-acl" value="nat.auto"/>

多域名注册的坑

force-register-domain 时有个隐性风险:如果同一个 Profile 下有多个 Gateway,其中一个要求 your_number@operator_a.com,另一个要求 your_number@operator_b.com,方案一会让你二选一,没办法同时满足。

这时候必须切到方案二,给每个 Gateway 单独配置域名。

运营商改了域名规则怎么办

运营商偶尔会调整域名校验规则——比如从要求精确匹配改为支持通配符,或者反过来。这种情况下你这边也需要跟着改。建议在配置里加注释,记录当初为什么选了这个域名格式,方便后续排查:

<!-- 2024-01 对接时运营商要求 From/To 使用 operator_trunk_domain.com -->
<!-- 联系人:运营商技术支持老王 -->
<param name="force-register-domain" value="operator_trunk_domain.com"/>

上线后评估

配置生效后需要观察三个维度:

  1. 注册稳定性sofia status profile external 连续几小时显示 REGED,没有反复跳 REG_FAIL

  2. 呼入呼出成功率:实际拨测几通电话,确认主被叫都能建立通话。如果注册成功但呼叫失败,问题可能在别处——比如 DTMF 格式、编解码协商、或者 SIP URI 格式。

  3. 日志告警:检查 /var/log/freeswitch/freeswitch.log,过滤关键词 REG_FAIL403auth 确认没有异常。

# 实时查看注册相关日志
tail -f /var/log/freeswitch/freeswitch.log | grep -E "(REGISTER|REGED|REG_FAIL|403)"

整个排查和修复过程的核心逻辑是:先确认是域名格式问题还是认证问题,再根据中继数量选择方案一或方案二,最后确保旧注册记录被清除。方案三永远留作排查阶段的临时工具,生产环境不启用。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

33 − = 27