FreeSWITCH 呼入呼出路由配置实战:SIP中继与拨号计划详解
简介:这份文档面向使用 FreeSWITCH 搭建 VoIP 通信平台的运维与开发人员,聚焦呼入、呼出路由配置这一关键环节,帮助读者理清拨号计划与 SIP 中继的配合方式。内容围绕 FreeSWITCH 事件驱动架构展开,讲解如何通过 XML 拨号计划定义外部来电的转发、语音邮箱或转接策略,并说明对等中继模式下外呼时填写本机 IP 与监听端口的做法,同时涉及中继认证、传输类型、负载均衡、错误重试与日志监控等实践要点。资源包共 1 个 doc 文件,约 221KB,以系统架构说明为主线,涵盖程序启动、消息分发、mod_sofia 模块组成与启动流程等章节,便于对照理解配置背后的运行机制。目前已有 6455 人学习,适合需要梳理 FreeSWITCH 路由配置思路、排查中继对接问题的读者参考。
1. 从一通打不出去的电话说起:FreeSWITCH 呼入呼出路由配置到底卡在哪
上周帮一个做客服系统的朋友排查问题,现象很典型:FreeSWITCH 装好了,软电话注册成功,内部分机互拨没问题,但一拨外线就静音,外面打进来直接进语音信箱。翻了一遍配置,sip_profiles里网关填了,dialplan里也写了规则,可就是不通。最后定位到两个点:一是外呼走的是对等中继模式,中继那头填的 IP 和端口跟本机实际监听的对不上;二是呼入的public上下文压根没匹配到网关送过来的号码。这类问题在 FreeSWITCH 部署里太常见了,装完能启动不代表能打电话,路由配置才是真正决定呼入呼出能不能跑通的那一层。这篇笔记按我自己的落地顺序来:先把版本和目录结构理清,再配 SIP 中继,然后分别写呼入和呼出的拨号计划,最后给一套验证和排查的方法。适合已经装好 FreeSWITCH、正卡在“能注册但打不通”这一步的开发者。
2. 装前先把版本和目录定下来:FreeSWITCH 1.2.7 的环境基线
FreeSWITCH 的版本差异会直接影响配置文件的位置和模块加载方式,所以动手改路由之前,先把版本和目录结构确认清楚,不然后面每一步都可能在错误的前提下折腾。
2.1 版本选择与依赖确认
这份笔记对应的基线是 FreeSWITCH 1.2.7,属于比较早期的稳定分支,配置文件以 XML 为主,模块通过modules.conf显式加载。现在新版本已经到 1.10.x,配置结构大体延续,但模块名和默认路径有调整,如果你装的是新版,下面涉及路径的地方要按实际安装目录替换。
安装前我一般会先确认三件事:操作系统位数、编译依赖、以及 SIP 协议栈是否随核心一起编进来了。FreeSWITCH 支持 32 位和 64 位平台,Linux 下常见做法是用源码编译,依赖里比较关键的是 APR、APR-util、SQLite、以及 Sofia-SIP。1.2.7 时代 Sofia-SIP 是作为库一起处理的,新版则更多依赖系统包或单独编译。
# 确认系统架构和已安装的关键依赖 uname -m ldconfig -p | grep -E "libapr|libsofia|libsqlite3" # 源码编译时的典型配置,前缀决定后续所有路径 ./configure --prefix=/usr/local/freeswitch \ --enable-core-odbc-support \ --disable-signalwire--prefix这个参数很关键,它决定了后面conf、log、db目录的根位置,我习惯统一放/usr/local/freeswitch,排查时路径好记。--enable-core-odbc-support是按需开的,如果你的路由里要用到数据库查询再开,否则可以先不加,减少编译变量。编译完成后执行make && make install,安装完先别急着改配置,用默认配置启动一次,确认核心能起来。
2.2 目录结构与配置文件分布
FreeSWITCH 的目录结构是理解路由配置的前提。核心可执行文件在src/switch.c里定义入口,启动后按SWITCH_GLOBAL_dirs里的路径去找配置。安装完成后,配置根目录下几个位置跟路由直接相关:
| 路径 | 作用 | 路由相关度 |
|---|---|---|
conf/freeswitch.xml | 主配置文件,聚合各子配置 | 高,决定加载哪些 XML |
conf/dialplan/ | 拨号计划,呼入呼出规则 | 最高 |
conf/sip_profiles/ | SIP профиль 与网关定义 | 高,中继在这里配 |
conf/autoload_configs/ | 模块自动加载配置 | 中,modules.conf在此 |
conf/directory/ | 用户目录,分机注册信息 | 中,影响认证 |
freeswitch.xml通过<X-PRE-PROCESS>把dialplan、sip_profiles、directory下的文件拼进来,所以改完子文件后要么重启,要么在控制台执行reloadxml。这里有个容易忽略的点:modules.conf在 1.2.7 的 Windows 平台位于conf/autoload_configs下,Linux 同样在这个目录,模块没加载的话,后面配了中继也不会生效。启动时核心会先加载集成模块(如CORE_SOFTTIMER_MODULE、CORE_PCM_MODULE),再遍历modules.conf加载其余模块,mod_sofia必须在这个列表里。
<!-- conf/autoload_configs/modules.conf 片段 --> <configuration name="modules.conf" description="Modules"> <modules> <load module="mod_console"/> <load module="mod_logfile"/> <load module="mod_sofia"/> <!-- SIP 端点,呼入呼出都靠它 --> <load module="mod_dialplan_xml"/> <!-- XML 拨号计划解析 --> </modules> </configuration>mod_sofia是 SIP 应用层和 Sofia-SIP 协议栈之间的桥,它负责处理注册、呼叫建立和信令收发;mod_dialplan_xml负责解析dialplan下的 XML 规则。这两个没加载,路由配置写得再对也没用。改完modules.conf后,控制台里show modules能看到已加载列表,确认这两个在列再往下走。
3. 配 SIP 中继:对等中继模式下 IP 和端口的填法
呼入呼出能不能通,中继这一层是地基。摘要里提到外呼用的是对等中继模式,意思是 FreeSWITCH 作为对等节点直接和网关或另一台 SIP 服务器交互,不经过注册,靠 IP 和端口识别对端。这种模式下配置的重点是网关地址、本机监听地址、以及上下文归属。
3.1 网关定义与对等中继参数
网关配置放在conf/sip_profiles/external/下,一个网关一个 XML 文件。对等中继模式不需要register,关键是realm、proxy、from-domain这几个参数填对。我一般会先确认本机实际监听的 IP 和端口,再往网关里填,避免出现“中继那头填的 IP 跟本机监听对不上”这种低级但高频的坑。
<!-- conf/sip_profiles/external/gw_pstn.xml --> <gateway name="gw_pstn"> <param name="proxy" value="192.0.2.10"/> <!-- 网关实际地址 --> <param name="realm" value="192.0.2.10"/> <!-- 对等模式填网关地址 --> <param name="register" value="false"/> <!-- 对等中继不注册 --> <param name="caller-id-in-from" value="true"/> <param name="from-domain" value="192.0.2.10"/> <param name="context" value="public"/> <!-- 呼入落到 public 上下文 --> </gateway>proxy是信令实际发往的地址,对等模式下就是网关 IP;realm在对等模式里通常和proxy一致,用于认证域匹配,不注册时它更多起标识作用。register设成false是对等中继和注册式中继的分水岭,注册式需要用户名密码定期注册,对等式靠 IP 信任。context决定从这条中继进来的呼叫落到哪个拨号计划上下文,呼入规则就写在public里。from-domain影响外呼时 From 头里的域,有些网关会校验这个字段,填错会被拒。
本机监听地址在conf/sip_profiles/internal.xml和external.xml里通过sip-ip、sip-port控制。对等中继场景下,网关那头填的“我的 FreeSWITCH 的 IP 和端口”必须和这里一致:
<!-- conf/sip_profiles/external.xml 关键参数 --> <param name="sip-ip" value="192.0.2.20"/> <!-- 本机对外 IP --> <param name="sip-port" value="5080"/> <!-- 对外监听端口 --> <param name="context" value="public"/>sip-ip填本机实际对外地址,多网卡时别填成回环;sip-port默认 5060,如果和内部 profile 冲突或网关指定了非标端口,就改成对应值。改完执行sofia profile external restart让 profile 重新加载,控制台里sofia status能看到 profile 的 RUNNING 状态和监听地址。
3.2 中继连通性验证
配完中继别急着写拨号计划,先验证信令能不能到对端。最直接的办法是用sofia status gateway gw_pstn看网关状态,对等模式没有注册状态,重点看State和CallsOUT、CallsIN计数。再进一步,用originate手动发起一个测试呼叫:
# 在 fs_cli 控制台执行,向网关发起测试呼叫 originate sofia/gateway/gw_pstn/1001 &echo这条命令让 FreeSWITCH 通过gw_pstn网关呼叫1001,接通后执行echo应用(回放音频)。如果信令通但媒体不通,会听到静音;如果信令都不通,控制台会直接报错,常见的是INVALID_GATEWAY或DESTINATION_OUT_OF_ORDER。前者说明网关名写错或没加载,后者多半是对端地址端口不对。这一步能过,说明中继层没问题,再往下写路由才有意义。
4. 呼入路由:public 上下文里怎么接住外部来电
呼入路由的本质是:外部呼叫从中继进来,落到public上下文,拨号计划按被叫号码匹配规则,决定转到哪个分机、语音信箱还是 IVR。配不好最常见的表现就是“打进来直接进语音信箱”或者“响一声就断”。
4.1 public 上下文的匹配逻辑
conf/dialplan/public.xml是呼入规则的入口。FreeSWITCH 收到中继来的呼叫后,用被叫号码去匹配public上下文里的extension。匹配靠condition里的destination_number,支持正则。我一般先写一条兜底规则,把所有进来的呼叫先接住,再按号码段细分,这样排查时至少知道呼叫有没有进到拨号计划。
<!-- conf/dialplan/public.xml --> <include> <extension name="inbound_from_gw"> <condition field="destination_number" expression="^(\d+)$"> <action application="log" data="INFO 呼入号码 $1"/> <action application="transfer" data="$1 XML default"/> </condition> </extension> </include>这段规则的意思是:任何纯数字被叫号码,先记一条日志,然后transfer到default上下文继续处理。$1是正则捕获组,对应完整号码。transfer的第二个参数XML指定拨号计划类型,第三个default是目标上下文。这样拆的好处是呼入的“接住”和“落地”分开,public只负责识别来源和初步分流,具体转到哪个分机在default里配,职责清晰,出问题也好定位。
如果网关送过来的号码带了前缀或者格式不统一,就在expression里做清洗,比如^0?(\d{4,})$去掉前导零再捕获。这里有个血泪经验:别在public里直接写死转某个分机,一旦号码段变了就得改多处,用transfer转到default再匹配,改一处就够。
4.2 呼入落地到分机与失败兜底
default上下文里配具体落地规则。分机注册在directory下,拨号计划里用user/分机号桥接。呼入落地通常还要考虑无人接听、忙、离线几种情况的兜底,不然呼叫会悬着。
<!-- conf/dialplan/default.xml 片段 --> <extension name="inbound_to_ext"> <condition field="destination_number" expression="^(10\d{2})$"> <action application="set" data="call_timeout=30"/> <action application="set" data="hangup_after_bridge=true"/> <action application="bridge" data="user/$1"/> <action application="voicemail" data="default $1"/> </condition> </extension>call_timeout=30设 30 秒振铃超时,超时后继续执行下一条action;hangup_after_bridge=true保证桥接成功后挂断主叫腿,避免残留通道;bridge到user/$1即对应分机;桥接失败(无人接、忙、离线)就落到voicemail留言。这套组合是我在呼入场景里用得最稳的写法,比单纯bridge多了兜底,用户感知上不会“打进来没反应”。
呼入还有一个容易忽略的点:网关送来的呼叫可能带sip_h_前缀的自定义头,需要透传给分机时用export或set处理。如果分机侧要显示主叫号码,确认caller-id-in-from在中继里开了,否则主叫显示可能是网关地址而不是真实号码。
5. 呼出路由:从分机拨号到网关出局的完整链路
呼出比呼入多一层:分机先拨号,default上下文匹配到外呼规则,再通过网关出局。链路长了,出问题的点也多,常见的是拨了没反应、或者出局后对端拒接。
5.1 外呼规则的号码匹配与网关选择
外呼规则写在default上下文,按被叫号码前缀区分走哪个网关。比如内部分机拨0开头走 PSTN 网关,拨其他走另一个中继。匹配用destination_number正则,动作里用bridge到sofia/gateway/网关名/号码。
<!-- conf/dialplan/default.xml 外呼片段 --> <extension name="outbound_pstn"> <condition field="destination_number" expression="^0(\d{7,15})$"> <action application="set" data="effective_caller_id_number=1001"/> <action application="set" data="effective_caller_id_name=Reception"/> <action application="bridge" data="sofia/gateway/gw_pstn/$1"/> </condition> </extension>^0(\d{7,15})$匹配以 0 开头、后跟 7 到 15 位数字的号码,捕获组$1是去掉前导 0 后的实际号码,桥接时拼到网关后面。effective_caller_id_number和effective_caller_id_name设置出局主叫显示,很多网关会校验主叫格式,不设的话可能显示成内部分机号被拒。这里号码长度范围按实际网关要求调,PSTN 一般 7 到 15 位,填窄了会漏匹配,填宽了可能把内部短号也匹配进去,我一般会先用log打一条确认匹配结果再正式桥接。
多个网关时,可以在一条规则里用bridge的|分隔做顺序尝试,或者用distributor做负载。简单场景下顺序尝试就够:
<action application="bridge" data="sofia/gateway/gw_pstn/$1|sofia/gateway/gw_backup/$1"/>前一个网关失败会自动尝试后一个,适合主备线路。注意|两侧不要有空格,否则会被当成号码的一部分。
5.2 出局认证与失败处理
对等中继不注册,但有些网关仍要求出局时带认证信息,这时在网关 XML 里补username、password,并在bridge前set对应的sip_auth_username、sip_auth_password。如果网关返回 401/407,控制台日志里能看到SIP/2.0 401 Unauthorized,基本就是认证没带或不对。
出局失败的处理,我习惯在bridge后加hangup_cause判断,按不同原因走不同提示或重试:
<extension name="outbound_pstn"> <condition field="destination_number" expression="^0(\d{7,15})$"> <action application="set" data="call_timeout=45"/> <action application="bridge" data="sofia/gateway/gw_pstn/$1"/> <action application="playback" data="ivr/ivr-call_rejected.wav"/> </condition> </extension>bridge失败后会继续执行playback放一段提示音再挂断,比直接静音挂断体验好。call_timeout=45是出局振铃超时,PSTN 出局一般给长一点,30 到 60 秒之间按线路质量调。如果要做失败重试,用transfer转到另一条规则,别在同一个 extension 里堆太多bridge,逻辑会乱。
6. 避坑与排查:路由配完不通时先看这几处
路由配置的坑大多集中在“配置写了但没生效”和“生效了但匹配错”两类。下面几条是我实际踩过的,按现象、原因、解决整理,遇到不通时按顺序过一遍,能省不少时间。
6.1 常见踩坑记录
现象:分机拨外线没反应,控制台无报错。原因多半是default上下文里外呼规则的destination_number没匹配上,比如号码长度超出正则范围,或者前导 0 没处理。解决:在规则最前面加一条log动作打印destination_number,用fs_cli看实际进来的号码长什么样,再调正则。我一般会临时把expression放宽到^(\d+)$确认能匹配,再逐步收紧。
现象:呼入直接进语音信箱,分机不响。原因通常是public上下文没匹配到,呼叫落到了默认的voicemail规则,或者transfer的目标上下文写错。解决:fs_cli里console loglevel debug打开调试,看public上下文匹配了哪条 extension,确认destination_number和正则是否对得上。另一个高频原因是中继的context参数没设成public,呼叫进了default上下文,自然匹配不到呼入规则。
现象:外呼能出局但对方听不到声音。原因是对等中继下媒体地址协商问题,sip-ip填了回环或内网地址,SDP 里带的地址对端不可达。解决:确认external.xml里sip-ip是本机对外地址,必要时设ext-rtp-ip和ext-sip-ip显式指定外部地址。媒体问题信令日志看不出来,得抓包或者看sofia status里的 SDP 信息。
现象:改完配置不生效。原因是只改了 XML 没重载。dialplan改动用reloadxml,sip_profiles改动用sofia profile external restart,modules.conf改动必须重启进程。我吃过一次亏,改完网关以为自动生效,结果折腾半小时才发现 profile 没重启。
现象:网关状态显示FAILED或呼叫被拒。对等模式重点查proxy地址和端口是否和网关实际监听一致,以及from-domain是否被对端校验。注册式则查用户名密码。sofia status gateway gw_pstn的State字段和fs_cli里的sofia loglevel all输出是主要线索。
6.2 日志与调试入口
排查路由问题,fs_cli是主要工具。几个常用命令:console loglevel debug打开详细日志,sofia loglevel all打开 SIP 信令日志,show channels看当前通道状态,show calls看活动呼叫。信令层问题看sofia日志里的SIP/2.0响应码,拨号计划问题看switch_core_state_machine相关的匹配日志。日志量大的时候,用grep过滤destination_number或网关名,比从头翻快得多。
7. 装完怎么验证:一套能跑通的呼入呼出自检流程
配置写完不等于跑通,我习惯按固定顺序做一遍自检,从内到外逐层确认,哪一层断了就停在哪一层排查,不跳步。这套流程跑完,呼入呼出基本就稳了。
第一步,确认模块和 profile 状态。fs_cli里执行show modules,确认mod_sofia和mod_dialplan_xml在列;sofia status看internal和external两个 profile 都是RUNNING,监听地址和端口符合预期。这一步不过,后面都是白搭。
第二步,验证中继连通。sofia status gateway gw_pstn看网关状态,再用originate sofia/gateway/gw_pstn/测试号码 &echo手动发起一次呼叫,确认信令能到对端。对等中继没有注册状态,重点看呼叫计数和错误码。
第三步,验证呼入。从外部号码拨入,fs_cli里console loglevel debug观察呼叫是否落到public上下文、匹配了哪条 extension、transfer到default后是否桥接到分机。分机振铃、接通、挂断整条链路走一遍,再测一次无人接听看是否落到语音信箱。
第四步,验证呼出。分机拨一个外部号码,观察default上下文匹配、bridge到网关、对端是否振铃接通。重点确认主叫显示正确、媒体双向可听。如果对端能听到你但你听不到对端,回头查sip-ip和 RTP 地址。
第五步,回归测试。把内呼、呼入、呼出各跑两遍,确认没有偶发失败。偶发问题多半和超时参数、网关并发限制有关,call_timeout和网关侧的并发数要对得上。
| 验证项 | 命令/操作 | 通过标准 |
|---|---|---|
| 模块加载 | show modules | mod_sofia、mod_dialplan_xml在列 |
| Profile 状态 | sofia status | 两个 profile 均 RUNNING |
| 中继连通 | originate ... &echo | 信令通,无错误码 |
| 呼入链路 | 外部拨入 + debug 日志 | 匹配 public,桥接到分机 |
| 呼出链路 | 分机拨外线 | 出局成功,双向媒体正常 |
这套自检我每次改完路由配置都会走一遍,尤其是reloadxml之后,别假设改动一定生效。有一次改完呼入规则忘了reloadxml,自检第一步就发现日志里还是旧规则,省了后面一堆瞎猜。从那以后我每次动完dialplan或sip_profiles,都强制先跑一遍sofia status和一条originate测试,确认底层通了再测业务链路。希望帮到你。
本文还有配套的精品资源,点击获取