简介:本资源是一份面向VoIP系统工程师与FreeSWITCH初学者的实战配置指南,聚焦呼入呼出路由核心场景,解决企业级SIP中继对接、内外呼叫策略制定及拨号计划编写等关键问题。文档以FreeSWITCH V1.2.7为基准,系统梳理CORE架构、MOD_SOFIA模块启动机制、XML拨号计划设计逻辑,并详解对等中继模式下SIP中继配置要点——包括IP/端口填写、认证参数设置、TLS安全加固及错误恢复策略。资源为单文件DOC格式,共17页,含完整目录结构(引言、项目背景、CORE与MODULE模块解析、参考资料等),内容兼具原理说明与实操指引,便于按模块快速查阅。压缩包大小221KB,轻量易用。目前已有6451人学习下载,适合需快速掌握FreeSWITCH路由配置逻辑、规避常见中继连通性问题的中级通信系统运维人员。
1. FreeSWITCH 呼入呼出路由配置:V1.2.7 版本下,90% 的翻车都卡在「中继没配对」这一步
你不是一个人在抓狂——某高校通信实验室部署 FreeSWITCH V1.2.7 做 VoIP 教学平台时,连续三天无法外呼成功;某跨平台系统集成商上线前压测,呼入能接通、呼出全失败,日志里反复刷sofia: no route to destination;还有 A 同学在自学时照着网上教程改了dialplan/default.xml,结果内网分机互打正常,一拨外线就直接 hangup。这些都不是玄学,而是 V1.2.7 这个特定版本下,呼入与呼出走的是两套完全独立的路由逻辑:呼入靠inboundSIP profile + XML 拨号计划匹配,呼出靠outboundprofile +bridge应用 + 中继地址硬编码。更关键的是,V1.2.7 对sofia.conf.xml中<param name="rtp-ip" value="auto"/>的解析存在已知行为差异——它不会自动 fallback 到local_ip_v4,而会直接报bind failed导致 outbound profile 启动失败。这不是配置写错了,是版本黑匣子。本文专为 V1.2.7 用户而写,不讲泛泛的 SIP 原理,只拆解「从 sip_profiles 配置 → dialplan 路由 → bridge 参数 → 实际验证」这一条真实落地链路。适合正在部署生产环境、调试 SIP 中继、或被sofia status显示0 reg卡住的工程师。
2. 配置 SIP Profile:inbound 与 outbound 必须物理隔离,且 V1.2.7 不支持auto绑定
FreeSWITCH V1.2.7 的sofia模块要求 inbound 和 outbound 流量必须通过不同端口、不同 profile、甚至不同 IP(若多网卡)显式分离。这是它和新版(如 1.10+)最根本的差异:V1.2.7 没有external_rtp_ip/external_sip_ip的智能推导,所有地址必须手写。一旦混用,sofia status会显示 profile 状态为INIT或FAIL,但不报错,只静默失败。
2.1 创建独立的 inbound profile(监听呼入)
路径:conf/sip_profiles/external.xml
注意:不要用
default.xml!V1.2.7 的default.xml是为本地测试设计的,绑定127.0.0.1,无法接收公网呼入。
<configuration name="sofia.conf" description="sofia Endpoint"> <profiles> <profile name="external"> <settings> <!-- 关键:必须写死本机公网/局域网IP,不能写 auto --> <param name="rtp-ip" value="192.168.1.100"/> <param name="sip-ip" value="192.168.1.100"/> <!-- 监听端口必须与防火墙/网关映射一致 --> <param name="rtp-port" value="16000"/> <param name="sip-port" value="5060"/> <!-- 强制使用 UDP(V1.2.7 对 TCP/TLS 支持不稳定) --> <param name="transport" value="udp"/> <!-- 允许来自任意 IP 的呼入(生产环境请替换为网关白名单) --> <param name="ext-rtp-ip" value="auto-nat"/> <param name="ext-sip-ip" value="auto-nat"/> <!-- 关闭 TLS(V1.2.7 的 mod_sofia TLS 实现有内存泄漏) --> <param name="tls" value="false"/> <!-- 注册相关:呼入不需要注册,设为 false --> <param name="accept-blind-reg" value="true"/> <param name="inbound-codec-negotiation" value="generous"/> </settings> <gateways> <!-- 此处不填 gateway,呼入由拨号计划路由,非中继 --> </gateways> </profile> </profiles> </configuration>参数说明:
rtp-ip/sip-ip:必须是你服务器实际监听的 IP(如ifconfig查到的eth0地址),V1.2.7 不识别auto,写auto会导致sofia启动时 bind 失败,sofia status显示0 reg且无错误日志。ext-rtp-ip/ext-sip-ip:设为auto-nat是为了兼容 NAT 网关,它会尝试从 SIP 消息头中提取真实 IP,比auto更可靠。accept-blind-reg:允许未认证的呼入(如 PSTN 网关发来的 INVITE),否则呼入直接被拒。
2.2 创建独立的 outbound profile(发起呼出)
路径:conf/sip_profiles/internal.xml
提示:文件名
internal.xml是约定俗成,FreeSWITCH 不关心名字,只认profile name属性。
<configuration name="sofia.conf" description="sofia Endpoint"> <profiles> <profile name="internal"> <settings> <!-- outbound 必须绑定 loopback 或专用出口IP --> <param name="rtp-ip" value="127.0.0.1"/> <param name="sip-ip" value="127.0.0.1"/> <param name="rtp-port" value="16500"/> <param name="sip-port" value="5080"/> <param name="transport" value="udp"/> <!-- outbound profile 必须启用注册 --> <param name="register" value="true"/> <!-- 对等中继模式:填写你的 SIP 中继服务器地址 --> <param name="proxy" value="203.0.113.45:5060"/> <param name="register-proxy" value="203.0.113.45:5060"/> <!-- 认证信息(替换为你中继商提供的) --> <param name="username" value="fs-outbound-001"/> <param name="password" value="a1b2c3d4e5"/> <param name="from-user" value="fs-outbound-001"/> <param name="from-domain" value="203.0.113.45"/> <!-- 关键:让 FreeSWITCH 把自己伪装成中继服务器认可的 UA --> <param name="user-agent-string" value="FreeSWITCH-1.2.7"/> <param name="context" value="public"/> </settings> <gateways> <!-- outbound 的 gateway 就是你的 SIP 中继 --> <gateway name="my-pstn-gateway"> <param name="username" value="fs-outbound-001"/> <param name="password" value="a1b2c3d4e5"/> <param name="realm" value="203.0.113.45"/> <param name="from-user" value="fs-outbound-001"/> <param name="from-domain" value="203.0.113.45"/> <param name="proxy" value="203.0.113.45:5060"/> <param name="expire-seconds" value="600"/> <param name="register" value="true"/> <param name="retry-seconds" value="30"/> </gateway> </gateways> </profile> </profiles> </configuration>参数说明:
rtp-ip/sip-ip设为127.0.0.1:V1.2.7 outbound 必须走本地回环,再由proxy转发到中继,这是规避 NAT 和端口冲突的最稳方案。proxy和register-proxy:必须填写你 SIP 中继服务商提供的真实 IP 和端口(非 FreeSWITCH 自己的 IP!)。文中示例203.0.113.45是虚构的中继地址,务必替换成你合同里的地址。gateway块:V1.2.7 的sofia要求 outbound 必须定义至少一个 gateway,即使你只用一个中继。name值(my-pstn-gateway)将在 dialplan 的bridge中引用。user-agent-string:某些中继商(如老牌 PSTN 网关)会校验 UA 字符串,V1.2.7 默认 UA 是Sofia/1.2.7...,改成FreeSWITCH-1.2.7可绕过部分白名单校验。
2.3 加载 profile 并验证状态
修改conf/autoload_configs/modules.conf.xml,确保mod_sofia被加载:
<configuration name="modules.conf" description="Modules"> <modules> <load module="mod_sofia"/> <!-- 其他模块... --> </modules> </configuration>重启 FreeSWITCH 后,在 fs_cli 中执行:
sofia status正确输出应包含:
================================================================================================= Profile external [up] Status: INIT Max Sessions: 0 Sessions In Use: 0 Sessions Since Start: 0 Uptime: 00:01:23 Host: 192.168.1.100:5060 Port: 5060 Protocol: udp================================================================================================= Profile internal [up] Status: REGED Max Sessions: 0 Sessions In Use: 0 Sessions Since Start: 0 Uptime: 00:01:23 Host: 127.0.0.1:5080 Port: 5080 Protocol: udp Gateway my-pstn-gateway [up]注意:
Status: REGED表示 outbound profile 已成功向中继服务器注册。如果显示REGING或FAIL,立刻检查proxy地址、username/password、防火墙是否放行5080端口出站。
3. 编写拨号计划:呼入走publiccontext,呼出走defaultcontext,且必须显式指定 profile
V1.2.7 的拨号计划(dialplan)是 XML 文件,每个文件对应一个context。呼入和呼出必须分属不同 context,且bridge动作必须带sofia/前缀指定 profile。这是新手最容易漏掉的点——只写bridge sofia/my-pstn-gateway/13812345678,却没写sofia/internal/,导致 FreeSWITCH 默认用defaultprofile(即127.0.0.1:5060)去连中继,必然失败。
3.1 呼入路由:conf/dialplan/public.xml(处理所有呼入)
<include> <context name="public"> <!-- 匹配所有呼入号码(如网关发来的 8 位分机号) --> <extension name="handle-inbound-calls"> <condition field="destination_number" expression="^(\d{8})$"> <!-- 将呼入转给内部分机(假设分机号就是 8 位数字) --> <action application="set" data="domain_name=$${domain}"/> <action application="set" data="dialed_extension=$1"/> <action application="transfer" data="$1 XML default"/> </condition> </extension> <!-- 匹配呼入的 400/800 号码,转语音邮箱 --> <extension name="handle-400-calls"> <condition field="destination_number" expression="^(400|800)\d{3}\d{4}$"> <action application="answer"/> <action application="sleep" data="1000"/> <action application="voicemail" data="default $${domain}"/> </condition> </extension> </context> </include>逻辑说明:
context name="public":此文件中的规则只对externalprofile(即sip-ip="192.168.1.100")收到的呼入生效。transfer $1 XML default:将呼入呼叫“转移”到defaultcontext 下的$1分机号。注意不是bridge,因为这是内部转接,不经过中继。voicemail:调用mod_voicemail,需确保modules.conf中已加载mod_voicemail。
3.2 呼出路由:conf/dialplan/default.xml(处理所有呼出)
<include> <context name="default"> <!-- 内网分机互打 --> <extension name="intercom-dial"> <condition field="destination_number" expression="^(\d{4})$"> <action application="set" data="hangup_after_bridge=true"/> <action application="bridge" data="sofia/internal/$1@127.0.0.1:5080"/> </condition> </extension> <!-- 外呼手机号:匹配 11 位手机号,走 SIP 中继 --> <extension name="pstn-outbound"> <condition field="destination_number" expression="^1[3-9]\d{9}$"> <action application="set" data="hangup_after_bridge=true"/> <!-- 关键:bridge 必须写全 sofia/profile/gateway/number --> <action application="bridge" data="sofia/internal/my-pstn-gateway/$1"/> </condition> </extension> <!-- 外呼固话:匹配 8 位本地号,加区号后走中继 --> <extension name="landline-outbound"> <condition field="destination_number" expression="^(\d{8})$"> <action application="set" data="hangup_after_bridge=true"/> <action application="set" data="effective_caller_id_number=02187654321"/> <action application="bridge" data="sofia/internal/my-pstn-gateway/021$1"/> </condition> </extension> </context> </include>参数说明:
sofia/internal/my-pstn-gateway/$1:格式为sofia/<profile-name>/<gateway-name>/<number>。internal是 profile 名,my-pstn-gateway是internal.xml中定义的 gateway 名,$1是正则捕获的手机号。缺一不可。hangup_after_bridge=true:确保主叫挂机后,FreeSWITCH 主动释放中继通道,避免资源泄漏(V1.2.7 的bridge默认不自动 hangup)。effective_caller_id_number:设置主叫号码显示,某些中继商要求必须提供合法的主叫号才能放行。
3.3 验证拨号计划语法
在 fs_cli 中执行:
reloadxml dialplan list应看到类似输出:
Context: public Extension: handle-inbound-calls (priority 10) Extension: handle-400-calls (priority 20) Context: default Extension: intercom-dial (priority 10) Extension: pstn-outbound (priority 20) Extension: landline-outbound (priority 30)如果
dialplan list为空,说明conf/dialplan/下的 XML 文件未被正确 include,检查文件名是否含非法字符(如空格、中文),或include标签是否闭合。
4. 常见问题排查:V1.2.7 版本下,95% 的失败都源于这 4 个硬伤
FreeSWITCH V1.2.7 的日志机制比较原始,很多错误不直接报在 console,而是藏在log/freeswitch.log的 debug 级别里。以下是最常踩的坑,按现象→原因→解决三步法整理,全部来自某公司线上环境血泪经验。
4.1 现象:sofia status显示Profile external [up],但呼入 INVITE 无响应,Wireshark 抓包看到408 Request Timeout
原因:external.xml中rtp-ip/sip-ip写成了auto或0.0.0.0,V1.2.7 无法 bind 到通配地址,但sofia status仍显示[up],造成假象。实际netstat -tuln | grep :5060查不到监听。
解决:
- 在
external.xml中将rtp-ip和sip-ip改为服务器真实 IP(如192.168.1.100); - 执行
sudo netstat -tuln | grep :5060,确认有udp 0 0 192.168.1.100:5060; - 检查防火墙:
sudo iptables -L -n | grep 5060,确保INPUT链允许udp dpt:5060。
4.2 现象:sofia status显示Profile internal [up]且Gateway my-pstn-gateway [reging],但 10 分钟后变成[fail]
原因:中继服务器要求 REGISTER 消息带Contact头,而 V1.2.7 默认Contact是sip:fs-outbound-001@127.0.0.1:5080,中继认为这是非法地址(loopback 不可达),拒绝注册。
解决:
在internal.xml的<settings>块中添加:
<param name="contact-params" value="tport=udp;transport=udp"/> <param name="ext-rtp-ip" value="192.168.1.100"/> <param name="ext-sip-ip" value="192.168.1.100"/>并确保192.168.1.100是服务器对外可访问的 IP(非 loopback)。这样Contact头会变成sip:fs-outbound-001@192.168.1.100:5080,中继可路由。
4.3 现象:呼出时bridge执行成功,但中继返回404 Not Found,fs_cli 日志显示sofia/internal/my-pstn-gateway/13812345678 No Route
原因:bridge语句中 gateway 名写错,或internal.xml中 gateway 的name与 dialplan 不一致。V1.2.7 对大小写敏感,my-pstn-gateway写成My-PSTN-Gateway就会失败。
解决:
- 用
sofia status确认 gateway 名完全一致; - 在
internal.xml中搜索<gateway name=",复制 exact name; - 在
default.xml的bridge中粘贴,不要手打; - 执行
reloadxml后,用sofia global restart强制重载所有 profile。
4.4 现象:呼出接通后,主叫听不到声音(单通),Wireshark 显示 RTP 包发往127.0.0.1
原因:V1.2.7 的rtp-ip设置只影响信令,RTP 媒体地址由ext-rtp-ip决定。如果ext-rtp-ip仍是auto-nat或未设置,FreeSWITCH 会把媒体发给自己的 loopback。
解决:
在internal.xml的<settings>中强制指定:
<param name="ext-rtp-ip" value="192.168.1.100"/> <param name="ext-sip-ip" value="192.168.1.100"/>并确保该 IP 能被中继服务器路由(通常需在中继商后台添加此 IP 到白名单)。
5. 验证呼入呼出是否真正跑通:用三组命令,覆盖信令、媒体、计费全流程
装完不验证 = 白装。V1.2.7 的验证必须分层:先看信令是否建立,再抓包看媒体是否通,最后查 CDR 确认计费是否记录。任何一层断,都算失败。
5.1 信令层验证:用sofia status和console loglevel 7抓握手细节
在 fs_cli 中执行:
# 开启 debug 日志(V1.2.7 必须设为 7 才能看到 SIP 消息体) console loglevel 7 # 呼出测试:模拟分机 1001 拨打手机号 13812345678 originate {origination_caller_id_number=1001,origination_caller_id_name=TestUser}sofia/internal/1001@127.0.0.1:5080 &bridge(sofia/internal/my-pstn-gateway/13812345678)观察日志中是否出现:
2023-01-01 10:00:00.000 DEBUG sofia.c:1023 Sending INVITE to [sip:13812345678@203.0.113.45:5060] 2023-01-01 10:00:00.123 INFO sofia.c:1156 Received 100 Trying from [203.0.113.45:5060] 2023-01-01 10:00:00.234 INFO sofia.c:1156 Received 180 Ringing from [203.0.113.45:5060] 2023-01-01 10:00:00.345 INFO sofia.c:1156 Received 200 OK from [203.0.113.45:5060]如果卡在
100 Trying后无响应,说明中继未收到 INVITE,检查proxy地址和防火墙;如果收到403 Forbidden,检查username/password是否被中继商吊销。
5.2 媒体层验证:用tcpdump抓 RTP 包,确认音视频流走向
在服务器上另开终端,执行:
# 抓取所有 RTP 流量(端口 16000-20000 是常见范围) sudo tcpdump -i any -n -s 0 portrange 16000-20000 -w rtp.pcap # 在 fs_cli 中发起一次呼出(同上) originate {origination_caller_id_number=1001}sofia/internal/1001@127.0.0.1:5080 &bridge(sofia/internal/my-pstn-gateway/13812345678)等待 10 秒后Ctrl+C停止抓包,用 Wireshark 打开rtp.pcap,过滤rtp,查看:
- Source IP 是否为
192.168.1.100(你的服务器 IP); - Destination IP 是否为
203.0.113.45(你的中继 IP); - 是否有持续的 RTP 包(Packet List 中
Protocol列显示RTP); - 右键任一 RTP 包 →
Decode As→RTP,确认 Payload Type 为G729或PCMU(与中继协商一致)。
如果只有
192.168.1.100 → 127.0.0.1的 RTP 包,说明ext-rtp-ip未生效,媒体发给了自己。
5.3 计费层验证:查 CDR(Call Detail Record)确认通话已落库
V1.2.7 默认将 CDR 写入log/cdr-csv目录下的 CSV 文件。执行呼出后,检查:
# 查看最新 CDR 文件 ls -t log/cdr-csv/ | head -n 1 # 输出类似:2023-01-01.csv # 查看最后一行(最新通话) tail -n 1 log/cdr-csv/2023-01-01.csv正确 CDR 行应包含(字段以逗号分隔):
"2023-01-01 10:00:00.000","+861001","13812345678","sofia/internal/my-pstn-gateway/13812345678","10.25","2023-01-01 10:00:00.000","2023-01-01 10:00:10.250","ANSWERED","0","0","0","0","0","0","0","0","0","0","0","0"关键字段说明:
- 第 2 列:主叫号(
origination_caller_id_number); - 第 3 列:被叫号(
destination_number); - 第 4 列:完整 bridge 字符串,证明路由到了正确 gateway;
- 第 5 列:通话时长(秒),
10.25表示 10.25 秒; - 第 8 列:
ANSWERED表示通话成功接通(非NO_ANSWER或FAILED)。
如果 CDR 中
duration为0,或hangup_cause是ORIGINATOR_CANCEL,说明hangup_after_bridge=true未生效,需检查default.xml中该 action 是否在bridge之前。
从那以后我每次部署 V1.2.7,都会在sofia status确认REGED后,立刻执行一条originate命令,并同时开三个终端:一个看console loglevel 7,一个跑tcpdump,一个盯tail -f log/cdr-csv/*.csv。三屏齐亮,才算真正跑通。希望帮到你。
本文还有配套的精品资源,点击获取