7101、7102已经能互拨,上一期也接好了IVR。这一期把一条SIP线路接进FS,让这两个已鉴权分机通过前缀9呼叫一个指定测试号码。做完要能区分:线路配置读进来了、注册成功了、电话真正打通了,这三个状态分别看什么。
配置按FreeSWITCH v1.10.12的vanilla模板和源码核对,未接真实运营商实测。示例里的账号、口令、域名和被叫都要在本机替换;国内线路的号码格式、主叫许可、并发和费用以开通资料为准。
先问清线路是哪一种
注册型线路:FS拿运营商账号向对方发送REGISTER,运营商记录联系地址。按IP鉴权的线路:双方按约定地址接入,通常不需要注册。register="false"只是不发REGISTER,并不会替你完成对端白名单、防火墙或入站ACL。
准备好SIP服务器与端口、UDP/TCP/TLS要求、鉴权账号和realm、允许的主叫、被叫格式、实际出口IP,以及一个经过授权的测试被叫。有些线路注册服务器和呼叫服务器不同,这两个地址也要分开确认。本期先采用线路已支持的UDP示例,TLS证书和公网NAT后面分期讲。
在fs_cli查目录与Profile:
eval $${conf_dir}
sofia status profile external
sofia status profile internal reg
下面用CONF表示第一条返回的目录。external必须存在且已运行;7101、7102使用的内部入口需对本次INVITE完成鉴权。仅有注册记录不代表所有后续请求都已鉴权。保留已有鉴权配置,本期不改监听端口或放宽来电来源。
gateway文件放在哪里
gateway是线路定义,不是新增一个SIP分机,更不是队列agent。vanilla的CONF/sip_profiles/external.xml在gateways内部包含external/*.xml,因此本期文件放CONF/sip_profiles/external/。已有这条包含指令时保持原样:
<gateways>
<!-- 位于external这个Profile内部,读取它所属的线路文件 -->
<X-PRE-PROCESS cmd="include" data="external/*.xml"/>
</gateways>
先备份父配置,并确认training_carrier_reg、training_carrier_ip未被使用。文件名方便管理,真正供bridge引用的是gateway的name。子文件使用include包裹gateway,不再写一套profile。
注册型线路的完整示例
新建CONF/sip_profiles/external/06_carrier_reg.xml:
<include>
<!-- 本地线路名,与后面bridge里的名称一致 -->
<gateway name="training_carrier_reg">
<!-- 运营商分配的SIP账号和口令,必须在本机替换 -->
<param name="username" value="YOUR_TRUNK_USER"/>
<param name="password" value="LOCAL_TRUNK_PASSWORD"/>
<!-- 呼叫目的服务器;此处为保留示例域名 -->
<param name="proxy" value="sip.carrier.example"/>
<!-- 鉴权域,按运营商给出的值填写 -->
<param name="realm" value="YOUR_AUTH_REALM"/>
<!-- From身份与鉴权账号可能不同,由线路要求决定 -->
<param name="from-user" value="YOUR_FROM_USER"/>
<param name="from-domain" value="sip.carrier.example"/>
<!-- 注册型线路发送REGISTER,示例按支持UDP的线路编写 -->
<param name="register" value="true"/>
<param name="register-transport" value="udp"/>
<!-- 请求注册有效期与失败重试间隔,单位均为秒 -->
<param name="expire-seconds" value="3600"/>
<param name="retry-seconds" value="30"/>
<!-- 来电继续进入public,不开放整个default -->
<param name="context" value="public"/>
</gateway>
</include>
username是线路账号,password是线路口令,和7101的分机密码没有关系。若口令含&、<或双引号,写XML属性时分别转成&、<、"这些实体;转义后的XML值仍应还原成原口令。保存后限制文件读权限,只让服务和必要运维账号访问。
proxy决定发往哪个SIP服务器;realm是Digest鉴权域,不一定是同一个字符串。from-user和from-domain决定From身份,运营商可能要求固定账号或已分配号码,填7101并不代表对方允许用它当公网主叫。主叫显示与相关信令头在第08期展开。
register=true开启注册;register-transport=udp选择注册使用的传输方式,它不是Profile监听设置,也不能据此认定所有呼叫都按同一传输发送。expire-seconds=3600请求一小时注册有效期,运营商可调整实际有效期;retry-seconds=30是失败重试间隔,单位都为秒。
context=public保留入站路由隔离。注册成功也不会自动生成04期的DID映射,回呼仍要核对实际收到的被叫和入口规则。本期主线只验收外呼。
按IP鉴权的线路怎么写
这类线路改用另一个文件CONF/sip_profiles/external/06_carrier_ip.xml,测试时按实际开通方式二选一。下面203.0.113.10是文档示例地址,不能当成真实服务器:
<include>
<!-- 与注册型二选一测试;203.0.113.10是文档地址,须替换 -->
<gateway name="training_carrier_ip">
<!-- 对端SIP地址及端口按线路资料填写 -->
<param name="proxy" value="203.0.113.10:5060"/>
<!-- 不发送REGISTER,运营商需另行放行本机实际出口IP -->
<param name="register" value="false"/>
<!-- 保留public入站路由;这个参数不自动生成ACL -->
<param name="context" value="public"/>
</gateway>
</include>
该版本register=false时可不填username/password。若运营商不注册但对INVITE仍发口令挑战,就不是“只靠IP”的情况,仍需补它要求的鉴权资料。运营商放行的是它看到的实际出口IP,不一定是FS网卡上的地址。
无注册线路通常显示NOREG,加载后本地状态可以为UP。这只表示本地gateway可用状态,不是一次实际拨号或对端放行的证明,后面仍要打一通授权测试电话。
给7101、7102加一条小范围外呼规则
在CONF/dialplan/default/新建06_training_outbound.xml。把YOUR_TEST_NUMBER替换为运营商接受的唯一测试被叫,纯数字且不带本地前缀9;保留外面的括号及^、$。未替换前这个示例不会匹配普通电话拨号。
<include>
<!-- 仅用于已鉴权内部分机的受控外呼测试 -->
<extension name="training-outbound-06">
<!-- 依据本次INVITE鉴权账号,不能用可改写的显示号码代替 -->
<condition field="${sip_auth_username}" expression="^(7101|7102)$"/>
<!-- YOUR_TEST_NUMBER替换为唯一授权测试被叫的纯数字号码 -->
<condition field="destination_number" expression="^9(YOUR_TEST_NUMBER)$">
<!-- 记录命中标记,便于关联新产生的外呼腿 -->
<action application="log" data="NOTICE training-outbound-06"/>
<!-- 限制本次bridge的等待,单位秒,不限制已接通通话时长 -->
<action application="set" data="call_timeout=30"/>
<!-- 9是本地接入前缀,捕获组$1才是发送给线路的被叫 -->
<action application="bridge" data="sofia/gateway/training_carrier_reg/$1"/>
<!-- 桥接返回后的测试流程到此结束 -->
<action application="hangup"/>
</condition>
</extension>
</include>
sip_auth_username取本次请求的鉴权账号,不使用caller_id_number作为身份凭据。若该变量为空,先查内部入口鉴权与通道变量来源,不是删掉第一条检查继续放行。该规则还依赖原系统正确的鉴权和路由范围,不代替完整的外呼权限体系。
第二条condition只匹配9加指定测试被叫,$1取得括号里的号码,去掉9后发给线路。用IP型线路测试时,只把bridge里的training_carrier_reg换成training_carrier_ip。call_timeout=30控制bridge等待,单位秒,不是接通后30秒自动挂断。这里不提前answer,让外呼接听阶段按正常桥接流程推进。
确认父default.xml仍包含default目录,也检查更早的宽规则是否已接走带9号码。完整的号码归一化、去前缀与补前缀在下一期讲,本期先把范围缩到一个被叫。真实线路测试可能计费,选自己能接听、已获授权的号码。
加载与验收分开做
新增文件后在fs_cli执行:
reloadxml
sofia profile external rescan
sofia status gateway training_carrier_reg
IP型测试将查询中的名称换为training_carrier_ip。reloadxml更新XML,rescan让运行中的Profile扫描新增gateway;只改文件并reloadxml,内存里的同名线路并不会替换。首次新增注册型线路应能看到注册过程,成功状态通常为REGED;NOREG用于不注册线路,不能按“没出现REGED”一概判断失败。
从7101拨9加替换后的测试号码,检查本地路由标记、运营商侧振铃、接听、双向语音和两端挂机,再由7102做同样检查。核对实际发出的被叫是否只去掉一个9,主叫是否符合线路许可。注册成功但没声音时,还要检查SDP、RTP地址端口和编码,注册流程并不携带通话媒体。
后续修改已有gateway参数,要安排线路可中断的维护窗口,先停用这条测试路由、结束相关通话,再删除旧的内存对象并重新扫描。killgw会影响这条线路的注册和新呼叫,不把它当成无影响的热更新:
sofia profile external killgw training_carrier_reg
等状态确认旧对象已移除后,再执行external rescan并查询。这个命令是标记删除,不能把输出的+OK当成删除已经全部完成。其他gateway不使用_all_,本期也不重启整个Profile。
模板没启用但常用的选项
鉴权名和账号不同时用auth-username。注册目标不同用register-proxy;呼叫还需经过独立代理时核对outbound-proxy。它们解决不同的地址问题,不能看到注册失败就全填成同一个地址。
ping可按秒发送OPTIONS,用于探测。运营商不响应或不支持这类探测时,启用后可能把本来能呼叫的线路标成DOWN;本例先省略,确认对端要求再配置。它也不代替通话和媒体验收。
extension、extension-in-contact影响注册Contact里的用户部分及相关入站寻址,不是给内部新增分机;更改后要与04期入站规则一起复验。模板中注释的参数也需查版本实现,不能认为放进gateway就一定生效,例如该版本的gateway参数解析未接收cid-type,本例不复制它。
常见卡点与回退
gateway查不到:查父include、Profile名字、XML错误和重名,再确认做过rescan。同名参数改了仍是旧值:看是否还保留旧对象,按维护步骤更新该线路。
反复401/407:一次挑战可能是正常鉴权步骤,要看携带凭据后的重试结果;继续失败再查账号、口令、realm和运营商限制。RFC 3261分别定义注册端与代理的鉴权挑战,不能只凭第一个401就认定密码错。超时无响应则先查地址解析、传输、出口放行和对端可达性。
REGED却呼叫被拒:核对被叫格式、允许主叫、余额与线路权限,并结合运营商具体响应定位。UP但无注册的IP线路打不出:核对对方看到的出口IP和白名单。接通后单通:从RTP地址、端口、编码继续排查,反复改注册口令解决不了这类问题。
回退时先移走本期外呼规则并reloadxml,停止产生新测试呼叫;等相关通话结束,再移走新增gateway文件、killgw对应名称并等它移除。若是修改旧线路则恢复原备份后rescan复验。只处理本期线路,复查7101/7102互拨、7190队列和7180菜单仍正常。
核验资料
配置依据官方v1.10.12线路模板、external包含结构、gateway参数实现及加载命令实现。当前gateway手册作补充;核验日期2026-11-03。完整线路验收仍需真实开通参数和受控测试环境。