OpenSIPS 3.4 安装与配置指南
OpenSIPS是一个成熟的开源SIP服务器,除了提供基本的SIP代理及SIP路由功能外,还提供了一些应用级的功能。
官网:openSIPS | Main / HomePage
安装opensips3.4
下载
git clone –recurse-submodules https://github.com/OpenSIPS/opensips.git -b 3.4 opensips-3.4
1
工具准备
sudo yum install -y mysql-devel libmicrohttpd-devel bison flex
1
若还是缺少工具或依赖,如遇到“make: flex: Command not found”错误,使用yum install -y flex安装缺失的工具的方式进行安装。
编译安装
默认编译
make all
修改编译参数
make menuconfig
该界面使用右箭头进入下一级,左箭头返回上一级,空格选中
编译选择数据库
Configure Compile Options–>Configure Excluded Modules–>db_mysql–>Configure Compile Options –> Save Changes
修改安装路径
Configure Compile Options–>Configure Install Prefix–>然后输入创建好的目录–>回车–>save changes
安装
Compile And Install OpenJIPS 回车安装
生成配置文件
配置文件参数
脚本类型 配置选项 描述
Residential Script(住宅型脚本) ENABLE_TCP OpenSIPS将监听TCP端口以接收SIP请求
ENABLE_TLS OpenSIPS将监听TLS端口以接收SIP请求
USE_ALIASES 允许SIP用户使用别名
USE_AUTH 对REGISTER和INVITE请求进行身份验证
USE_DBACC 在数据库中保存所有呼叫的ACC条目
USE_DBUSRLOC 持久存储用户位置信息在数据库中
USE_DIALOG 跟踪活动的SIP对话
USE_MULTIDOMAIN 支持多个用户域
USE_NAT 处理NAT问题,包括修复SIP消息和集成RTPProxy
USE_PRESENCE 作为Presence服务器运行
USE_DIALPLAN 使用拨号计划转换本地号码
VM_DIVERSION 未达订阅者的呼叫重定向到语音信箱
HAVE_INBOUND_PSTN 接受来自PSTN网关的呼叫(静态IP认证)
HAVE_OUTBOUND_PSTN 向PSTN网关发送数字拨号(静态IP定义)
USE_DR_PSTN 使用动态路由支持(LCR)进行PSTN互连
Trunking Script(中继型脚本) ENABLE_TCP OpenSIPS将监听TCP端口以接收SIP请求
ENABLE_TLS OpenSIPS将监听TLS端口以接收SIP请求
USE_DBACC 在数据库中保存所有呼叫的ACC条目
USE_DIALPLAN 使用拨号计划转换本地号码
USE_DIALOG 跟踪活动的SIP对话
DO_CALL_LIMITATION 限制每个中继的并行呼叫数量
Load-Balancer Script(负载均衡脚本) ENABLE_TCP OpenSIPS将监听TCP端口以接收SIP请求
ENABLE_TLS OpenSIPS将监听TLS端口以接收SIP请求
USE_DBACC 在数据库中保存所有呼叫的ACC条目
USE_DISPATCHER 使用DISPATCHER模块分发SIP流量
DISABLE_PINGING 不主动向所有目标发送ping(仅在检测到失败时发送)
生成方式
方法一
在源码中运行make menuconfig
Generate OpenSIPS Script–>Residential Script–>Configure Residential Script–>如下选择–>q返回上一层–>save residential script–>Generate Residential Script
cd /root/opensips/etc/opensips/
mv opensips.cfg opensips.cfg.old
mv opensips_residential_2018-5-3_1\:13\:3.cfg opensips.cfg (将自动生成的配置脚本重命名)
方法二(推荐)
安装目录下 sbin进入目录后执行./osipsconfig,后与方法一相同
使用menuconfig生成OpenSIPS配置脚本后,需在编辑器中修改’# CUSTOMIZE ME’注释处,以自定义侦听地址和数据库URL,然后保存并测试脚本。
安装opensips-cli
opensips-cli 工具部署 opensips 数据库。
下载
git clone https://github.com/opensips/opensips-cli opensips-cli
1
依赖安装
sudo yum install python36 python36-pip python36-devel gcc mysql-devel python36-mysql python36-sqlalchemy python36-pyOpenSSL
1
安装Python3.91
依赖
yum update -y
yum -y groupinstall "Development tools"
yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel
1
2
3
下载
wget https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tgz Python-3.9.1
1
编译安装
在Python-3.9.1目录下生成安装配置文件:
./configure –prefix=/usr/local/python3
1
然后执行编译和安装:
make && make install
1
使用
在使用处前加上:/usr/local/python3/bin/python3.9或是/usr/local/python3/bin/pip3.9
构建安装
cd ~/src/opensips-cli
local install (only visible to your user)
python3 setup.py install –user clean
system-wide install
sudo python3 setup.py install clean
可能出现问题及解决方案
/usr/src/opensips-cli/bin# ./opensips-cli
Traceback (most recent call last):
File "./opensips-cli", line 3, in <module>
from opensipscli import main
ImportError: No module named opensipscli
1
2
3
4
5
解决方案:需使用python3进行启动
sudo /usr/local/python3/bin/python3.9 setup.py install clean
1
清理安装
sudo rm -fr /usr/local/bin/opensips-cli /usr/local/lib/python3.6/dist-packages/opensipscli*
1
配置文件
vim ./opensips-cli/etc/default.cfg
[default]
log_level: WARNING
prompt_name: opensips-cli
prompt_intro: Welcome to OpenSIPS Command Line Interface!
prompt_emptyline_repeat_cmd: False
history_file: ~/.opensips-cli.history
history_file_size: 1000
output_type: pretty-print
communication_type: fifo
fifo_file: /tmp/opensips_fifo
选择模块添加数据库表结构
database_modules: ALL
数据库脚本目录
database_schema_path: /root/opensips-3.4/scripts/
数据库管理员账号
database_admin_url: postgres://root@localhost
database_admin_url: mysql://root@localhost
会新建数据库账号:opensips,密码:opensipsrw
database_url: postgres://opensips:opensipsrw@localhost
database_url: mysql://root:123321@192.168.88.131
数据库名称
database_name: opensips
domain: 192.168.88.131
plain_text_password: true
创建数据库
yum install opensips-mysql-module opensips-postgres-module opensips-sqlite-module opensips-berkeley-module
1
/usr/local/python3/bin/python3.9 opensips-cli -f /root/opensips-cli/etc/default.cfg -x database create
1
create: 创建数据库(可选指定名称),部署标准表。
drop: 删除数据库(可选指定名称)。
add: 在数据库中添加模块表,需指定模块名。
migrate: 转换数据库至新版本的OpenSIPS。
添加新模块,如 :rtpproxy,将创建表:rtpproxy_sockets
opensips-cli -x database add rtpproxy
启动、关闭与日志处理
OpenSIPS 服务可通过命令行或配置.service文件来进行systemctl管理
生成opensips.service
创建的 .service 文件需位于 /usr/lib/systemd/system 目录下
cd /usr/lib/systemd/system
vim opensips.service
[Unit]
Description=OpenSIPS is a very fast and flexible SIP (RFC3261) server
Documentation=man:opensips
After=network.target mysqld.service postgresql.service rtpproxy.service
[Service]
Type=forking
User=root
Group=root
RuntimeDirectory=opensips
RuntimeDirectoryMode=775
Environment=P_MEMORY=320 S_MEMORY=320
EnvironmentFile=-/etc/default/opensips
PermissionsStartOnly=yes
PIDFile=%t/opensips/opensips.pid
ExecStart=/user/local/opensips -P %t/opensips/opensips.pid -f /user/local/opensips/buss.cfg -m $S_MEMORY -M $P_MEMORY $OPTIONS
ExecStop=/usr/bin/pkill –pidfile %t/opensips/opensips.pid
Restart=always
TimeoutStopSec=30s
LimitNOFILE=262144
[Install]
WantedBy=multi-user.target
启动
/root/opensips/sbin/opensips -P /root/opensips/opensips.pid -f /root/opensips/etc/opensips/opensips.cfg -m 320 -M 320
1
常用命令选项
选项 说明
-f <file> 指定配置文件路径,默认为/root/opensips/etc/opensips/opensips.cfg
-c 仅检查配置文件是否有错误,不启动服务。
-C 检查配置文件及其包含的路由块的函数标志。
-F 守护进程模式,但主进程保持在前台。
-m <nr> 设置分配的共享内存大小(MB)。
-M <nr> 设置分配的包(pkg)内存大小(MB)。
-P <file> 创建并写入PID文件。
-p <pp_cmd> 携带一个系统命令,该命令用于预处理配置文件。
openSIPS |文档 / 模板化 opensips.cfg 文件 – 3.4
预处理工具 描述 示例命令
m4 宏处理器,用于配置文件中的宏展开 opensips -p "m4" -f opensips.cfg
sed 流编辑器,用于文本替换 opensips -p "sed ‘s/SEARCH_PATTERN/REPLACE_PATTERN/g’" -f opensips.cfg
awk 强大的文本分析工具,适用于复杂文本处理 opensips -p "awk ‘{print $0}’" -f opensips.cfg
Python 脚本语言,可编写复杂的配置文件处理逻辑 opensips -p "python script.py" -f opensips.cfg
Shell脚本 编写一系列命令的脚本,适用于复杂的预处理流程 opensips -p "/bin/bash script.sh" -f /path/to/opensips.cfg
关闭
/usr/bin/pkill –pidfile /root/opensips/opensips.pid
1
日志
编辑配置文件:打开OpenSIPS的配置文件,设置log_facility为LOG_LOCAL0:
log_facility=LOG_LOCAL0
1
添加日志规则:编辑/etc/rsyslog.conf,添加以下内容以将LOG_LOCAL0的日志重定向到指定文件:
vim /etc/rsyslog.conf
local0.* /var/log/opensips.log
1
2
3
通常都是放置在/var/log/目录下
创建日志目录和文件:确保/opt/opensips/logs/目录存在,并创建日志文件:
sudo mkdir -p /opt/opensips/logs
sudo touch /opt/opensips/logs/opensips.log
1
2
重启rsyslog和OpenSIPS:
systemctl restart rsyslog 或 service rsyslog restart//重启rsyslog服务
opensipsctl restart
1
2
实时查看日志:
使用tail命令实时查看OpenSIPS的日志文件:
tail -f /var/log/opensips.log
1
脚本语法
OpenSIPs 配置脚本有三个主要逻辑部分:
全局参数
modules 部分
路由逻辑
全局参数
openSIPS 核心参数
设置全局/核心参数,包括网络监听、传输协议、进程分叉、日志等全局选项。
disable_tcp = yes
listen = udp:192.168.3.40:5060
listen = udp:192.168.3.40:5070
fork = yes
children = 4
log_stderror = no
1
2
3
4
5
6
log_level
功能:控制 OpenSIPS 大部分日志输出的详细程度。
影响除 xlog() 函数之外的所有日志输出。
语法:log_level=<level>
参数:整数,范围 -3 到 4。
默认值:2
数值 级别
-3 警戒
-2 严重
-1 错误
1 警告
2 通知(默认)
3 信息
4 调试
示例:
log_level=1:仅记录重要消息。
log_level=4:记录大量调试信息。
xlog_level
功能:独立控制 xlog() 函数的日志详细程度。
语法:xlog_level = <level>
参数:整数,范围 -3 到 4(同log_level表)。
默认值:2
示例:xlog_level = 3 (调试级别)。
stderror_enabled
功能:启用或禁用将日志消息写入标准错误输出。
语法:stderror_enabled = <value>
<value>:可以是 yes、1(启用)或 no、0(禁用)。
默认值:yes 或 1(启用)
启用:默认情况下,日志消息发送到标准错误输出,适用于实时监控。
禁用:禁用后,日志不会输出到标准错误,适用于减少终端输出或精细管理。
syslog_enabled
功能:控制是否向 syslog 写入日志。
语法:syslog_enabled = [value]
默认值:no 或 disabled。
参数:yes(启用),no 或 disabled(禁用)。
示例:syslog_enabled = yes。
syslog
功能:控制 OpenSIPS 日志记录到 syslog 的设施级别。
syslog 配置见上述日志配置
用途:当需要将所有 OpenSIPS 日志重定向到不同的日志文件时非常有用。
默认值:LOG_DAEMON。
参数:例如 LOG_LOCAL0。
示例:syslog_facility=LOG_LOCAL0。
debug_mode
功能:激活 OpenSIPS 的调试模式。
语法:debug_mode = [value]
默认值:false 或 0(禁用)。
参数:1(启用),0(禁用)。
效果:保持在前台、设置日志级别为 4、日志输出到标准错误、启用核心转储、设置 UDP 和 TCP 工作进程数为 2。
注意:启用时会覆盖其他相关参数设置。
示例:debug_mode = 1。
log_stderror
功能:log_stderror 参数用于控制 OpenSIPS 的日志输出方式,但在 OpenSIPS 3.4 版本中已被弃用。
默认值:yes。
行为:
设置为 "no":相当于 stderror_enabled=no, syslog_enabled=yes。
设置为 "yes"(默认):相当于 stderror_enabled=yes, syslog_enabled=no。
示例配置:log_stderror = yes。
由于此参数已被弃用,建议使用 stderror_enabled 和 syslog_enabled 直接控制日志输出方式。
udp_workers
功能:设置每个 UDP 或 SCTP 接口的工作进程数。
默认值:8。
参数:
[number]:固定的工作进程数。
use_auto_scaling_profile [profile_name]:使用自动扩展配置。
udp_workers=16
udp_workers=4 use_auto_scaling_profile PROFILE_SIP
注意:特定接口可以覆盖全局设置。
disable_dns_blacklist
功能:控制 DNS 解析器黑名单功能。
语法:disable_dns_blacklist=[value]。
默认值:yes(禁用)。
参数:yes(禁用),no(启用)。
示例:disable_dns_blacklist=no。
dns_try_ipv6
功能:控制在 DNS 查找失败时是否尝试 IPv6(AAAA 记录)。
默认值:no。
参数:yes(启用),no(禁用)。
示例:dns_try_ipv6=yes。
socket
功能:设置 OpenSIPS 服务器监听的网络地址和端口。
语法:socket = [protocol]:[address][:[port]] [optional_parameters]
可多次设置:可在同一配置文件中多次设置 socket 参数,服务器将监听所有指定的套接字。
参数 描述
[protocol] 传输协议(如 udp, tcp, tls, bin, hep)。
[address] 监听地址(可以是 IP 地址、主机名、网络接口 ID 或 *)。
[port] 端口号(可选,默认使用协议模块定义的端口)。
选项参数
AS ip:port 设置接口的宣传 IP 和端口。
USE_WORKERS n 为特定套接字设置工作进程数。
ANYCAST 标记为 Anycast IP。
USE_AUTO_SCALING_PROFILE 设置动态调整 UDP 工作进程数的策略。
TAG 用于标识跨集群的套接字。
FRAG 允许分片。
REUSE_PORT 允许 TCP 基础上的套接字复用监听端口。
场景:
当需要配置 OpenSIPS 在特定地址和端口监听时使用。
当需要为特定接口设置额外参数时使用。
socket = udp:*
socket = udp:eth1
socket = tcp:eth1:5062
socket = tls:localhost:5061
socket = hep_udp:10.10.10.10:5064
socket = ws:127.0.0.1:5060 use_workers 5
socket = sctp:127.0.0.1:5060 as 99.88.44.33:5060 use_workers 3
socket = udp:10.10.10.10:5060 anycast
socket = udp:10.10.10.10:5060 use_workers 4 use_auto_scaling_profile PROFILE_SIP
Modules 部分
模块默认不自动加载,需通过loadmodule命令指定。模块可通过名称或完整路径(指向.so文件)加载。若未指定路径,则默认从安装目录/lib/opensips/modules加载。可通过mpath全局参数设置自定义路径。
加载模块后,使用modparam指令配置模块参数,具体参数及其类型可在模块文档的Parameters部分查看。
loadmodule "modules/mi_datagram/mi_datagram.so"
modparam("mi_datagram", "socket_name", "udp:127.0.0.1:4343")
modparam("mi_datagram", "children_count", 3)
mpath="/usr/local/opensips_proxy/lib/modules"
loadmodule "mi_datagram.so"
modparam("mi_datagram", "socket_name", "udp:127.0.0.1:4343")
modparam("mi_datagram", "children_count", 3)
loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
1
2
3
4
5
6
7
8
9
10
路由逻辑
OpenSIPS路由逻辑包括top routes和sub routes,分别用于直接处理SIP事件和从其他路由调用。
top routes
route(请求路由)
作用:处理SIP请求,接收外部请求被触发,后对请求进行处理。
类型:初始为无状态,可变为有状态。
默认操作:若未转发或回复,请求将被丢弃。需显式动作以发送回复或转发。
无状态模式:独立处理SIP请求,不保留状态,适合简单交互。
有状态模式:通过TM函数(如t_relay())跟踪状态,支持复杂功能如呼叫转移。
语法:route{…}或route[0]{…}
route {
if(is_method("OPTIONS")) {
send reply for each options request
sl_send_reply(200, "OK");
exit();
}
route(1);
}
route[1] {
forward according to uri
forward();
}
1
2
3
4
5
6
7
8
9
10
11
12
branch_route (分支路由)
作用:处理SIP请求每个分支。
触发:通过t_on_branch("index")进而注册新分支,t_relay触发
类型:有状态
默认操作:分支执行drop()则删除,反之分支通过自动转发(SIP请求中Via字段)
route {
lookup("location");
t_on_branch("1");
if(!t_relay()) {
sl_send_reply(500, "Internal Server Error");
}
}
branch_route[1] {
if($ru=~"10.10.10.10") {
discard branches that go to 10.10.10.10
drop();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
failure_route (失败路由)
作用:在失败路由中,可返回错误码给用户,重发请求,或修改状态码后回应用户。
触发:处理SIP请求返回的状态码≥300时,触发失败路由。通过t_on_failure("index")进行设置
类型:有状态
默认操作 :如果未生成新分支或未强制回复,则默认情况下,成功响应将返回给原始请求方。
route {
lookup("location");
t_on_failure("1");
if(!t_relay()) {
sl_send_reply(500, "Internal Server Error");
}
}
failure_route[1] {
if(is_method("INVITE")) {
call failed – relay to voice mail
t_relay("udp:voicemail.server.com:5060");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
onreply_route (回复路由)
作用:处理从网络接收到的 SIP 回复。
触发:当 OpenSIPS 从网络接收到 SIP 回复时触发。
类型:有状态(如果绑定到事务)或无状态(如果为全局回复路由)。
默认操作 :如果回复没有被丢弃(只有临时回复可以被丢弃),它将被注入并由事务引擎处理。
语法:onreply_route[options]{…}
global:捕获收到所有回复。 命名:onreply_route {…} 、onreply_route[global]{…}或 onreply_route[0] {…}
每个请求/事务 :捕获所有收到的属于某个事务的回复。准备:t_on_reply(事务名)。命名:onreply_route[事务名] {…}
per branch:仅捕获事务中属于某个分支的回复。准备:t_on_reply(事务名)。命名:onreply_route[事务名] {…}
route {
seturi("sip:bob@opensips.org"); # first branch
append_branch("sip:alice@opensips.org"); # second branch
t_on_reply("global"); # the "global" reply route
is set the whole transaction
t_on_branch("1");
t_relay();
}
branch_route[1] {
if ($rU=="alice")
t_on_reply("alice"); # the "alice" reply route
is set only for second branch
}
onreply_route {
xlog("OpenSIPS received a reply from $si\n");
}
onreply_route[alice] {
xlog("received reply on the branch from alice\n");
}
onreply_route[global] {
if (t_check_status("1[0-9][0-9]")) {
setflag(1);
log("provisional reply received\n");
if (t_check_status("183"))
drop;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
error_route(错误路由)
作用:处理失败的请求。
触发条件:SIP 请求解析错误或脚本断言失败。
注:仅在能够正常解析SIP消息第一行时触发,若第一行有错误则不会触发。
可用的伪变量:
$(err.class):错误类别(目前解析错误为 ‘1’)。
$(err.level) :错误的严重级别。
$(err.info) :描述错误的文本。
$(err.rcode) :推荐的回复码。
$(err.rreason) :推荐的回复原因短语。
error_route {
xlog("— error route class=$(err.class) level=$(err.level)
info=$(err.info) rcode=$(err.rcode) rreason=$(err.rreason) —\n");
xlog("— error from [$si:$sp]\n+++++\n$mb\n++++\n");
sl_send_reply("$err.rcode", "$err.rreason");
exit;
}
1
2
3
4
5
6
7
local_route(本地路由)
作用:处理修改由事务引擎(TM)内部生成的新 SIP 请求。
触发:事务引擎生成一个全新的请求时触发。
默认操作 :将请求发送出去。
local_route {
if (is_method("INVITE") && $ru=~"@foreign.com") {
append_hf("P-hint: foreign request\r\n");
exit;
}
if (is_method("BYE") ) {
acc_log_request("internally generated BYE");
}
}
1
2
3
4
5
6
7
8
9
startup_route (开始路由)
作用:执行初始化功能(如加载数据到缓存)
触发:OpenSIPS 启动时
startup_route {
avp_db_query("select gwlist where ruleid==1",$avp(i:100));
cache_store("local", "rule1", "$avp(i:100)");
}
1
2
3
4
timer_route(时间路由)
作用:执行定期或周期性的任务(如更新数据、检查系统状态等)
触发条件:由定时器工作进程触发,根据定义在路由名称旁边的时间间隔(以秒为单位)执行。
特点:允许存在多个 timer_route,它们可以在不同的运行间隔上执行。
timer_route[gw_update, 300] {
每5分钟(300秒)查询一次网关列表
avp_db_query("select gwlist where ruleid==1", $avp(i:100));
if ($avp(i:100) != "") {
将查询结果存储到共享变量
$shv(i:100) = $avp(i:100);
} else {
xlog("L_ERR", "Failed to update gwlist for rule 1\n");
}
}
1
2
3
4
5
6
7
8
9
10
event_route (事件路由)
作用:处理被触发的事件。
触发条件:由 OpenSIPS 事件接口触发的事件。
事件名称可以包含任何不带引号的字符串字符,但 建议遵循以下语法: E_MODULE_NAME_EXTRA_NAME
openSIPS | Documentation / Events Interface – 3.4
类型:无状态
event_route[E_PIKE_BLOCKED] {
xlog("The E_PIKE_BLOCKED event was raised\n");
}
1
2
3
sub routes
sub routes可命名,能从其他路由调用,调用时支持参数和返回值(不返回0)。它们像编程中的函数。
语法:
route[my_sub_route] {
子路由的代码
…
exit;
}
1
2
3
4
5
调用: route(route_name [, param1 [, param2 [, …] ] ] );
参数可以是 int、string 或伪变量
route {
…
$var(debug) = "DBUG:"
route(PRINT_VAR, $var(debug), "param value");
…
}
route[PRINT_VAR] {
$var(index) = 2;
xlog("$param(1): The parameter value is <$param($var(index))>\n");
}
1
2
3
4
5
6
7
8
9
10
11
可以通过$param(idx)进行获取路由参数,索引由1开始
返回值:返回一个数值代码,这个代码可以在调用它的路由中被检查。
route[my_sub_route] {
执行操作
…
return 1; # 返回一个数值代码
}
route[some_top_route] {
if (route(my_sub_route) != 1) {
如果 my_sub_route 返回的不是 1,执行其他操作
}
…
}
1
2
3
4
5
6
7
8
9
10
11
12
核心变量
openSIPS | Documentation / Core Variables – 3.4
$rc 和 $retcode
功能:存储最近函数调用的返回值
if (!mf_process_maxfwd_header(10) && $retcode==-1) {
sl_send_reply(483,"Too Many Hops");
exit;
};
1
2
3
4
$tU
功能:tU 是一个变量,用于引用 To 头字段中的用户名部分。
场景:用于获取或操作 SIP 通信中目标接收者的用户名。
$socket_in
功能:$socket_in 是一个只读变量,用于获取接收消息时访问套接字的各种属性或子字段。
格式: $socket_in(…)
子字段 描述
ip 套接字的 IP 地址
port 套接字的端口
proto 协议名称(如 “UDP”, “TCP”)
advertised_ip 宣传的 IP(若无则为 NULL)
advertised_port 宣传的端口(若无则为 NULL)
tag 内部标签/别名
anycast 是否使用任播 IP(1 是,0 否)
af 地址族(IPv4 为 “INET”,IPv6 为 “INET6”)
$rU
功能:$rU 是一个引用变量,指向SIP请求URI中的用户名部分。
特性:可读写变量。
用法:可以通过 $rU 访问或修改SIP请求URI中的用户名。
核心参数
openSIPS | Documentation / Core Parameters – 3.4
$fd
定义:SIP消息中From头部URI域名的引用(只读变量)
INVITE sip:alice@example.com SIP/2.0
Via: SIP/2.0/UDP client.example.net:5060;branch=z9hG4bKsd70d7
From: Bob <sip:bob@client.example.net>;tag=9fCda90
To: Alice <sip:alice@example.com>
Call-ID: a84b4c76e66710@pc33.example.com
CSeq: 314159 INVITE
Contact: <sip:bob@client.example.net:5060>
Max-Forwards: 70
User-Agent: Example SIP UA
Content-Type: application/sdp
Content-Length: 200
1
2
3
4
5
6
7
8
9
10
11
From 头字段包含域名 "client.example.net"。通过 $fd 变量,直接进行访问
…
$var(from_domain) = $fd; # $var(from_domain) 现在等于 "client.example.net"
…
1
2
3
$au
定义:提取了 Authorization 或 Proxy-Authorization 头字段中用户名username的部分。
作用:在认证过程中,当客户端提供了认证信息后,$au,用于身份验证和权限控制等业务逻辑处理。
REGISTER sip:server.example.com SIP/2.0
Via: SIP/2.0/UDP client.example.net:5060;branch=z9hG4bKsd70d7
From: Bob <sip:bob@client.example.net>;tag=9fCda90
To: Bob <sip:bob@client.example.net>
Call-ID: a84b4c76e66710@pc33.example.com
CSeq: 1 REGISTER
User-Agent: Example SIP UA
Contact: <sip:bob@client.example.net:5060>
Expires: 3600
Max-Forwards: 70
Proxy-Authorization: Digest username="bob", realm="example.com", nonce="ncc1701", uri="sip:server.example.com", response="d44042166c…", algorithm=SHA-256, qop=auth, nc=00000001, cnonce="0a4f113b"
Content-Length: 0
1
2
3
4
5
6
7
8
9
10
11
12
Proxy-Authorization 头字段包含用户名 "bob"。通过 $au 变量,可直接进行访问。
…
$var(username) = $au; # $var(username) 现在等于 "bob"
…
1
2
3
$fU
定义:引用了 From 头字段中 URI 的用户名username部分。
INVITE sip:alice@example.com SIP/2.0
Via: SIP/2.0/UDP client.example.net:5060;branch=z9hG4bKsd70d7
From: Bob <sip:bob@client.example.net>;tag=9fCda90
To: Alice <sip:alice@example.com>
Call-ID: a84b4c76e66710@pc33.example.com
CSeq: 314159 INVITE
Contact: <sip:bob@client.example.net:5060>
Max-Forwards: 70
User-Agent: Example SIP UA
Content-Type: application/sdp
Content-Length: 200
1
2
3
4
5
6
7
8
9
10
11
From 头字段包含用户名 "Bob"。通过 $fU 变量,可进行访问该用户名
…
$var(username) = $fU; # $var(username) 现在等于 "Bob"
…
1
2
3
$rd
定义:$rd 是一个读写变量,引用了 SIP 请求 URI 中的域名部分。
INVITE sip:alice@example.com SIP/2.0
Via: SIP/2.0/UDP client.example.net:5060;branch=z9hG4bKsd70d7
From: Bob <sip:bob@client.example.net>;tag=9fCda90
To: Alice <sip:alice@example.com>
Call-ID: a84b4c76e66710@pc33.example.com
CSeq: 314159 INVITE
Contact: <sip:bob@client.example.net:5060>
Max-Forwards: 70
User-Agent: Example SIP UA
Content-Type: application/sdp
Content-Length: 200
1
2
3
4
5
6
7
8
9
10
11
请求 URI 中的域名是 "example.com"。通过 $rd 变量,直接进行访问或是修改
if ($rd == "example.com") {
Do something specific for example.com
} else {
Do something else
}
修改请求 URI 中的域名
$rd = "newdomain.com";
1
2
3
4
5
6
7
8
核心函数
openSIPS | Documentation / Core Functions – 3.4
force_rport
作用:用于在 SIP 消息的 Via 头中添加 rport 参数,强制后续消息通过原始端口路由。
解决问题:NAT 环境下的通信问题。
rport参数:标识客户端实际发送SIP消息时使用端口号,确保 SIP 响应通过客户端实际端口返回,特别是在NAT环境
语法:force_rport()
route {
…
force_rport();
…
}
1
2
3
4
5
exit
功能:停止执行配置脚本,与return(0)相同。
语法:exit()
xlog
功能:用于记录日志。
语法:xlog([log_level, ]format_string)
log_level 数值 描述
L_ALERT -3 非常严重的错误,需要立即采取行动
L_CRIT -2 严重的错误,可能会导致服务不可用
L_ERR -1 默认级别,错误,可能会影响正常操作
L_WARN 1 警告,可能存在问题但不一定会影响服务
L_NOTICE 2 通知,正常但重要的系统事件
L_INFO 3 信息性消息,有助于了解系统状态
L_DBG 4 调试信息,用于诊断问题
setflag
功能:针对整个消息(含多分支),非单一分支进行设置标记。
注意:标记消息用于进行特殊处理或维护状态
语法:setflag("flag");
flag:要设置标志名称
示例
setflag("NAT_PING");
1
为当前处理消息设置 NAT_PING 标志,后续处理中,采取特定行为处理带有 NAT_PING 标志消息
setbflag
功能:用于为特定的分支(branch)设置一个在OpenSIPS内部处理的标志(flag)
语法:setbflag(flag, [branch_idx])
flag: 静态字符串,标志名称
branch_idx: 设置标志的分支索引,默认为0
为分支索引为1的分支设置NAT_PING标志
setbflag( "NAT_PING",1);
或者,为默认的RURI分支(分支索引0)设置NAT_PING标志
setbflag("NAT_PING"); // 这等同于 setbflag("NAT_PING",0)
后续可以通过if (isflagset("NAT_PING"))来检查标志是否被设置
注意:对于分支标志,应使用isbflagset而不是isflagset来检查
1
2
3
4
5
6
7
8
注意:在检查分支标志时,应使用isbflagset函数而不是isflagset
isflagset通常用于检查消息级别的标志,而isbflagset则用于检查特定分支的标志
is_myself
功能:检查host(可含port)是否匹配OpenSIPS监听地址之一。
监听:采用socket核心参数,如:socket=udp:192.168.21.58
语法:is_myself(host, [port])
返回值:true: 匹配,false: 不匹配
if (is_myself("$rd", $rp)) {
xlog("L_INFO", "the request is for local processing\n");
}
1
2
3
drop
语法:drop();
作用:停止执行配置脚本并更改之后隐式操作。
调用函数后,branch_route丢弃分支(branch_route隐式操作是转发)。
调用函数后,onreply_route 丢弃临时回复(onreply_route 隐式操作是根据 Via 标头向上游发送回复)
onreply_route {
if($rs == "183") {
drop();
}
}
1
2
3
4
5
isflagset
功能:isflagset函数用于检测当前处理的消息中是否设置了某个标志位。
语法:isflagset(string)
flag(字符串,静态):要检查的标志位名称。
场景:此函数可用于条件判断,依据标志位的状态来决定程序逻辑的流向。
if (isflagset("NAT_PING")) {
log("flag NAT_PING is set\n");
}
1
2
3
如果名为NAT_PING的标志位被设置,那么将会记录一条消息表明该标志位已设置。
isbflagset
功能:isbflagset函数用于检测特定分支上是否设置了指定的标志。
语法:isbflagset(flag, [branch_idx]);
flag:要检测的标志名称。
branch_idx:用于标识要检测标志的分支。默认为0,表示RURI分支。
返回值
真(true):如果指定标志在指定分支上被设置。
假(false):如果指定标志在指定分支上未被设置。
场景:用于检测特定分支上的标志状态,通常在处理SIP请求时用于条件判断。
if (isbflagset("NAT_PING", 1)) {
log("flag NAT_PING is set in branch 1\n");
}
1
2
3
使用isbflagset函数来检测编号为1的分支上是否设置了NAT_PING标志,并根据结果记录日志信息。
模块手册
记录在项目中使用到的模块
openSIPS | Documentation / Function Index 3.4
rr Module
该模块包含记录路由逻辑
loose_route
功能:检测路由模式(严格/宽松),有Route头则按头路由(true),无或仅本地则按RURI路由(false)。
注意:loose_route用于路由决策,不直接执行转发;转发由后续的t_relay等逻辑在路由脚本中完成。
语法:loose_route();
场景:处理对话内请求( ACK、BYE、reINVITE 等),非对话请求,如果这些请求具有预加载的路由集。
示例
//在对话中的连续请求应该按照记录路由确定的路径来走
if ( !loose_route() ) {
//不接受没有Route头的请求
send_reply(404,"Not here");
exit;
}
1
2
3
4
5
6
严格路由和松散路由
严格路由:请求的Request-URI必须匹配代理服务器URI,否则报错。
快递员直达
松散路由:请求可以经过任意route到达目的地,只要最终到达即可。
朋友转寄,经过多人手,到达目的地
区分方式:
Route头部有;lr参数表示松散路由。
没有;lr参数表示严格路由。
check_route_param
功能:检查本地Route头部的URI参数是否与给定的正则表达式匹配。
前置:在执行loose_route()后可用。
语法:check_route_param(re)
re (字符串):要匹配的正则表达式。
返回值:匹配成功:true;不匹配:false
场景:REQUEST_ROUTE
//如果Route头部的URI参数包含"nat=yes",则设置标志6。
if (check_route_param("nat=yes")) {
setflag(6);
}
1
2
3
4
record_route
作用:添加新的 Record-Route 头字段到 SIP 消息中,并置于现有 Record-Route 头字段之前,用于记录请求路径。
语法:record_route([string])
参数:不带参数则默认添加一个 Record-Route 头字段,可选参数格式为 ;name=value。
场景: REQUEST_ROUTE、BRANCH_ROUTE 和 FAILURE_ROUTE
…
record_route();
…
1
2
3
add_rr_param
功能:add_rr_param函数用于向Record-Route URI添加参数(参数必须是“;name=value”格式)。
语法:add_rr_param(param);
param(字符串):要添加的URI参数,必须遵循“;name=value”格式。
场景:REQUEST_ROUTE、BRANCH_ROUTE和FAILURE_ROUTE
…
add_rr_param(";nat=yes");
…
1
2
3
使用add_rr_param函数向Record-Route URI添加参数。
dialog Module
用于管理OpenSIPS中的通话会话,跟踪状态,存储信息,并可按需结束会话。
$DLG_status
作用:返回当前处理的顺序请求对应的会话状态。
前置:在执行loose_route()后可用。
语法:$DLG_status
状态值 描述
NULL 未找到会话
1 创建未回复
2 收到临时回复,待最终回复
3 最终回复已发,待ACK
4 最终回复与ACK均已确认
5 会话结束
//如果存在一个对话框,但当前请求验证无效
if ( $DLG_status!=NULL && !validate_dialog() ) {
xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n");
exit;
}
1
2
3
4
5
validate_dialog
功能:验证请求是否属于有效会话。
有效会话:已建立、已确认且处于活跃状态的会话,允许进行中继呼叫。
语法:validate_dialog()
返回码 描述
true 存在有效会话
-1 CSEQ序列号无效
-2 远程目标无效
-3 路由集无效
-4 其他错误(如解析错误、无对话框等)
…
if (has_totag()) {
loose_route();
if ($DLG_status!=NULL && !validate_dialog()) {
xlog("in-dialog bogus request \n");
} else {
xlog("in-dialog valid request – $DLG_dir !\n");
}
}
…
1
2
3
4
5
6
7
8
9
10
create_dialog
作用:用于为当前处理的请求创建对话(dialog)。请求必须是初始请求INVITE。
语法: create_dialog([flags]);
标志 描述
B 到达对话生命周期后,双向触发 BYE 消息。
P 每隔 options_ping_interval 秒向呼叫方发送 OPTIONS 消息。
p 每隔 options_ping_interval 秒向被叫方发送 OPTIONS 消息。
R 每隔 reinvite_ping_interval 秒向呼叫方发送 RE-INVITE 消息。
r 每隔 reinvite_ping_interval 秒向被叫方发送 RE-INVITE 消息。
E 检测到 SIP 赛跑条件后,在 race_condition_timeout 秒后结束通话。
SIP 赛跑条件:多个 SIP 消息几乎同时到达,导致状态冲突。
多个标志:多个标志可以组合使用,如 "BPp" 启用所有三种标志。
注意事项:RE-INVITE 和 OPTIONS 不能同时用于单个对话腿,若同时提供则仅使用 RE-INVITE。
返回值:
true:如果对话成功创建或对话已预先创建。
false:如果对话创建失败。
在SIP中,对话正式建立需等到200 OK响应。但在如OpenSIPS的中间件里,收到INVITE时即创建对话的内部记录,以便后续管理
if (is_method("INVITE")) {
create dialog with timeout
if ( !create_dialog("B") ) {//
send_reply(500,"Internal Server Error");
exit;
}
do_accounting("db");
}
1
2
3
4
5
6
7
8
9
10
11
options_ping_interval
定义:设置 OpenSIPS 生成对话内 OPTIONS 探测消息的时间间隔(秒),默认30秒。
用途:定期发送 OPTIONS 消息,维持对话状态。
语法:modparam("dialog", "options_ping_interval", integer)
…
modparam("dialog", "options_ping_interval", 20)
…
1
2
3
reinvite_ping_interval
定义:设置 OpenSIPS 生成对话内 RE-INVITE 探测消息的时间间隔(秒),默认300。
注意:确保 RE-INVITE 事务的超时时间小于 reinvite_ping_interval,以避免探测消息无限重试。
用途:定期发送 RE-INVITE 消息,维持对话状态。
语法:modparam("dialog", "reinvite_ping_interval", integer)
…
modparam("dialog", "reinvite_ping_interval", 600)
…
1
2
3
race_condition_timeout
定义:如果使用 E 标志创建对话,并发生 SIP 赛跑条件,则对话在 race_condition_timeout 秒后终止(默认 5 秒),支持 (200OK vs CANCEL) 和 (early BYE vs 200OK)。
语法:modparam("dialog", "race_condition_timeout", int)
…
modparam("dialog", "race_condition_timeout", 1)
…
1
2
3
$DLG_dir
功能:返回请求方向。
返回:被叫方请求返回 “upstream”,主叫方请求返回 “downstream”,无对话返回 NULL。
适用:仅限序列请求,执行 loose_route() 后可用。
Acc Module
用于将交易信息计入不同的后端系统,如syslog、SQL和AAA。
do_accounting
功能:用于指定会计处理的方式和位置。
语法:do_accounting(type, [flags], [table])
type(类型间用|分割) 描述
log syslog会计
db 数据库会计
aaa AAA特定会计
evi 事件接口会计
flags(类型间用|分割) 描述描述
cdr 启用对话级CDR存储,检测BYE后记录
missed 记录未接来电,首个未接后停用,需failure_route重激活以追踪多个
failed 交易失败(≥300状态)时记录
table:指定会计处理的表;替换旧的table_avp参数。
场景:REQUEST_ROUTE、FAILURE_ROUTE、BRANCH_ROUTE、LOCAL_ROUTE
示例
…
if (!has_totag()) {
if (is_method("INVITE")) {
/* 在数据库和syslog中启用CDR和未接来电会计;
- 数据库会计应在"my_acc"表中执行 */
do_accounting("db|log", "cdr|missed", "my_acc");
}
}
…
if (is_method("BYE")) {
/ 通过aaa将会在完成SIP事务成功后进行记录 /
do_accounting("aaa");
}
…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
www_challenge
功能:www_challenge函数用于生成包含摘要认证挑战的WWW-Authorize头字段,并将其放入服务器响应中,促使用户代理计算凭证并重新发送请求。
语法:www_challenge(realm[, qop[, algorithms]])
realm(字符串):提示用户的不透明字符串,通常为服务器域名。
qop(字符串,可选):定义为auth或auth-int,用于增强安全性。
algorithms(字符串,可选):提供给用户代理的摘要算法列表,默认为MD5。
返回值:该函数无返回值。
场景:此函数常用于HTTP/SIP认证过程中,当服务器需要用户代理提供身份验证信息时使用。
if (!www_authorize("siphub.net", "subscriber")) {
www_challenge("siphub.net", "auth,auth-int", "MD5,SHA-512-256");
}
1
2
3
如果www_authorize函数未能成功验证用户,则会使用指定的realm、qop及algorithms值来发起一个摘要认证挑战。
acc_db_request
功能:报告请求状态到数据库。
语法:acc_db_request(comment, table);
参数:
comment:包含回复码和原因的注释字符串。
table:目标数据库表名。
场景:REQUEST_ROUTE、FAILURE_ROUTE、BRANCH_ROUTE、LOCAL_ROUTE。
acc_db_request("Some comment", "Some table");
acc_db_request("$T_reply_code $(<reply>rr)", "acc");
1
2
Auth_db Module
用于数据库认证,需与auth模块搭配使用,不可单独使用。若需RADIUS认证,选择auth_radius模块。
loadmodule "auth.so"
loadmodule "auth_db.so"
1
2
www_authorize 用于服务器直接验证用户凭证。
proxy_authorize 用于代理服务器验证请求者的身份。
proxy_authorize
功能:对 SIP 请求从指定数据库表验证用户名和密码进行 digest 认证。
场景:proxy_authorize 主要用于代理服务器场景,即当请求通过一个代理服务器时,代理服务器需要验证请求者身份的情况。
语法:proxy_authorize(realm, table)
参数名 类型 描述
realm string 通常为服务器域名;空字符串时,从请求头 From 头部字段域生成。可含伪变量
table string 查找用户名和密码的表,如subscribers
错误码 描述
-5 通用错误,无回复
-4 请求中无凭证
-3 nonce已过时
-2 密码错误(用户有效)
-1 无效用户
场景:REQUEST_ROUTE
//如果 proxy_authorize 函数失败,将调用 proxy_challenge 函数自动生成 realm 并再次发起挑战。
if (!proxy_authorize("", "subscriber")) {
proxy_challenge("", "auth");
exit;
}
1
2
3
4
5
www_authorize
功能:通过realm来确定账号密码,后到table中查询验证数据。验证失败时需调用 www_challenge 重新发起认证挑战。
场景:www_authorize 主要用于终端用户直接与服务器进行交互的情况。
语法:www_authorize(realm, table)
realm:用于指示用户为特定的域名从而提供提供用户名和密码,通常表现为域名,若为空,REGISTER 请求用 To 头域,其他请求用 From 头域。
table:存储用户名和密码的表名,通常为(subscriber)。
错误代码 描述
-5 通用错误
-4 无凭证
-3 过期的 nonce 值
-2 密码错误
-1 用户不存在
…
if (!www_authorize("siphub.net", "subscriber"))
("siphub.net", "auth");
…
1
2
3
4
5
6
db_does_uri_exist
功能:db_does_uri_exist函数用于检查给定的SIP URI是否对应一个在指定表中存在的用户。
语法:db_does_uri_exist(uri, table);
uri:要测试的SIP URI。必须包含用户名部分才能进行有效的检查,允许使用变量。
table:用于搜索URI的表名(通常是SUBSCRIBER表)。
返回值:
真(true):如果URI对应的用户存在于指定的表中。
假(false):如果URI对应的用户不存在于指定的表中。
场景:REQUEST_ROUTE,用于验证URI是否对应一个有效的注册用户。
if (db_does_uri_exist($ru, "subscriber")) {
用户存在,执行相应操作
…
} else {
用户不存在,执行错误处理
sl_send_reply(404, "Not Found");
exit;
}
1
2
3
4
5
6
7
8
使用db_does_uri_exist函数来检查URI是否存在,并根据结果采取相应的行动。
Auth Module
提供支持其他认证相关功能的模块,并能通过用户名和密码进行认证。
proxy_challenge
功能:用于生成包含摘要认证挑战的 Proxy-Authorize 头,促使用户代理计算凭证并重试认证。
语法:proxy_challenge(realm[, qop[, algorithms]])
参数 类型 描述
realm 字符串 通常为服务器域名;空字符串"",从请求头 From 头部字段域生成,对于 REGISTER 请求使用 To头部字段。
qop 字符串 值为 auth 或 auth-int。(分隔,)
algorithms 字符串 算法列表:MD5、MD5-sess、SHA-256、SHA-256-sess、SHA-512-256 和 SHA-512-256-sess。默认为空时提供 MD5 。(分隔,)
$var(secure_algorithms) = "sha-256,sha-512-256";
…
if (!proxy_authorize("", "subscriber")) {
…
proxy_challenge("", "auth", $var(secure_algorithms)); # 自动生成 Realm
不允许使用 MD5
}
1
2
3
4
5
6
7
consume_credentials
作用:移除已被授权的凭证,确保下游消息不包含这些凭证。
语法:consume_credentials();
调用时机:在 www_authorize 或 proxy_authorize 成功后调用。
if (www_authorize("", "subscriber")) {
consume_credentials();
}
1
2
3
tm Module
负责管理SIP事务的状态,确保正确的事务处理流程。主要通过 t_relay() 函数来设置事务状态,处理重传请求,并将响应与请求正确关联。
t_on_branch
语法:t_on_branch(branch_route)
作用:设置分支路由,每个分支独立执行。
场景:请求路由(REQUEST_ROUTE)、分支路由(BRANCH_ROUTE)、回复路由(ONREPLY_ROUTE)和失败路由(FAILURE_ROUTE)
可以设置多个t_on_branch,但是只有一个会被执行,后边的钩子会覆盖前边的。
route {
t_on_branch("1");
t_relay();
}
branch_route[1] {
if ($ru=~"bad_uri") {
xlog("dropping branch $ru \n");
drop;
}
if ($ru=~"GW_uri") {
append_rpid();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
t_on_failure
语法:t_on_failure(failure_route)
作用:定义失败路由块,事务失败时控制流将被重定向该路由块。
场景:REQUEST_ROUTE、BRANCH_ROUTE、ONREPLY_ROUTE 和 FAILURE_ROUTE 。
…
route {
t_on_failure("1");
t_relay();
}
failure_route[1] {
seturi("sip:user@voicemail");
t_relay();
}
…
1
2
3
4
5
6
7
8
9
10
11
t_relay
作用:将 SIP 消息有状态地转发(中继)到当前 URI 指示的目的地址。
语法: t_relay([flags],[outbound_proxy])
Flags可取值 描述
no-auto-477 禁用自动生成477 SIP响应,用于全局转发失败处理。
no-dns-failover 禁用DNS故障转移,仅使用第一个解析的IP。
pass-reason-hdr 在CANCEL请求中信任并传递Reason头。
allow-no-cancel 允许不取消所有分支,遵循RFC3841的no-cancel指示。
outbound_proxy :指定出站代理地址,格式:[协议:]主机[:端口]
错误代码 描述
-1 通用内部错误
-2 消息格式错误或无法解析
-3 没有可用的目的地或请求已被取消
-4 目的地地址无法解析或无法到达
-5 目的地地址被过滤(如黑名单)
-6 通用发送失败
场景:请求路由(REQUEST_ROUTE)和失败路由(FAILURE_ROUTE)
…
if (!t_relay()) {
sl_reply_error();
exit;
}
…
t_relay( ,"tcp:192.168.1.10:5060");
…
t_relay(0x1, "mydomain.com:5070");
…
1
2
3
4
5
6
7
8
9
10
t_check_trans
功能:检查当前SIP请求是否与一个事务相关联
语法:t_check_trans()
检查CANCEL对应INVITE事务,确保正确取消会话邀请,维护SIP通信一致性。
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();// 如果存在对应的 INVITE 事务,则进行相应处理(如转发 CANCEL 请求)
}
exit;// 无论是否转发,都退出当前处理流程
}
1
2
3
4
5
6
逐跳ACK(Hop-by-hop ACK)
定义:确认SIP协议中INVITE请求的非2xx响应,沿INVITE原路径反向至UAS,通知所有代理释放相关资源。
场景:非2xx响应触发UAC发送逐跳ACK(如3xx重定向、4xx错误、5xx服务器错误、6xx全局错误)时,通知所有代理。
处理:UAC发逐跳ACK反向至路径代理,代理停重传、释资源,直至UAS,处理会话终止或错误。
端到端ACK(End-to-end ACK)
定义:端到端ACK确认2xx成功,UAC直发UAS,中间代理仅转发,确认会话建立,开始媒体交换。
场景:当UAC接收到2xx响应时,会发送端到端ACK。
处理:UAS收ACK知UAC确2xx,会话继续,可交媒体如音视频
t_on_reply
功能:t_on_reply函数用于设置处理回复(临时或最终)的路由块。
语法:t_on_reply(reply_route);
reply_route:要调用的回复路由块名称。
注意事项:
仅适用于外部收到的回复,不适用于本地生成的回复。
从分支路由调用时,仅对该分支生效。
从非分支路由调用时,对整个交易生效。
一个交易只能设置一个有效的onreply_route。
对于临时回复(1xx),可通过drop()函数终止路由执行。
场景:REQUEST_ROUTE、BRANCH_ROUTE、ONREPLY_ROUTE和FAILURE_ROUTE
route {
seturi("sip:bob@opensips.org"); # 第一分支
append_branch("sip:alice@opensips.org"); # 第二分支
t_on_reply("global"); # 设置整个交易的回复路由
t_on_branch("1");
t_relay();
}
branch_route[1] {
if ($rU=="alice")
t_on_reply("alice"); # 设置第二分支的回复路由
}
onreply_route[alice] {
xlog("received reply from alice\n");
}
onreply_route[global] {
if (t_check_status("1[0-9][0-9]")) {
setflag(LOG_FLAG);
log("provisional reply received\n");
if (t_check_status("183"))
drop;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
展示了如何设置针对整个交易和特定分支的回复路由,并根据回复类型执行相应操作。
t_was_cancelled
功能: 检查 INVITE 事务是否被 UAC 取消。
语法: t_was_cancelled();
返回值: 若事务被取消返回 true,否则 false。
场景: 可在 ONREPLY_ROUTE 或 FAILURE_ROUTE 中使用。
if (t_was_cancelled()) {
log("transaction was cancelled by UAC\n");
}
1
2
3
nathelper Module
通过重写Contact头域和SDP信息,并提供状态化ping功能,来帮助解决NAT环境下的通信问题。
nat_uac_test
功能:检测 SIP 消息是否源自 NAT,通过检查私有 IP 地址、端口差异等,返回布尔值。
返回值:检查通过(源自NAT后)将返回true
语法:nat_uac_test(flag[,flag,….])
使用场景:REQUEST_ROUTE、ONREPLY_ROUTE、FAILURE_ROUTE和BRANCH_ROUTE
flag 描述
private-contact 检查Contact是否含私有/共享IPv4地址
diff-ip-src-via 比较Via与源IP地址是否一致
private-via 检查顶层Via是否含私有/共享IPv4地址
private-sdp 搜索SDP中的私有/共享IPv4地址
diff-port-src-via 比较源端口与Via端口是否不同
diff-ip-src-contact 比较Contact与源IP地址是否一致
diff-port-src-contact 比较Contact端口与源端口是否不同
carrier-grade-nat 检查Contact、Via和SDP是否含运营商级NAT地址
在REQUEST_ROUTE中检查是否使用了私有Contact头或SDP媒体IP地址
if (nat_uac_test("private-contact,private-sdp")) {
xlog("SIP消息来自NAT环境(Call-ID: $ci)\n");
在这里可以添加针对NAT环境的特殊处理逻辑
}
1
2
3
4
5
fix_nated_register
功能:用于在 NAT 环境下处理 SIP 注册请求,得知设备在NAT外实际地址允许该地址路由到本地。
语法:fix_nated_register()
场景:REQUEST_ROUTE
说明:生成包含源IP、端口和协议的 URI,并将其设置为注册200 OK响应中的 Contact 中 received ,以便正确路由到 NAT 后的设备。
REGISTER sip:192.168.88.131:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.88.1:59677;rport;branch=z9hG4bKPjf0d01e199f5146d3a61e71bd408fd06f
…
Contact: <sip:99911@192.168.88.1:59677;ob>
…
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.88.1:59677;received=192.168.88.1;rport=59677;branch=z9hG4bKPjf0d01e199f5146d3a61e71bd408fd06f
…
REGISTER sip:192.168.88.131:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.88.1:59677;rport;branch=z9hG4bKPjc1a99d9bc2fb4d37b0b9aba349f76602
…
Contact: <sip:99911@192.168.88.1:59677;ob>
…
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.88.1:59677;received=192.168.88.1;rport=59677;branch=z9hG4bKPjc1a99d9bc2fb4d37b0b9aba349f76602
…
Contact: <sip:99911@192.168.88.1:59677;ob>;expires=300;received="sip:192.168.88.1:59677"
…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在REQUEST_ROUTE中
if (method == "REGISTER") {
检查是否需要处理NAT
(这里可以根据实际情况添加更多逻辑,比如检查请求头中是否已存在received参数等)
如果需要,则使用fix_nated_register()函数
fix_nated_register();
后续处理逻辑…
}
1
2
3
4
5
6
7
8
fix_nated_contact
功能:用于 NAT 环境中修改 SIP 消息的 Contact 头,确保包含正确的源地址和端口,响应可以正确地路由回请求方。
从而允许NAT(网络地址转换)环境中SIP设备能够正确接收后续的消息
语法:fix_nated_contact([uri_params])
uri_params :要添加到修改后的Contact URI中的参数列表
场景:REQUEST_ROUTE,ONREPLY_ROUTE,BRANCH_ROUTE
在REQUEST_ROUTE中,根据User-Agent头字段的内容来决定是否向Contact URI添加额外的参数:
if (search("User-Agent: Cisco ATA.*")) {
如果User-Agent是Cisco ATA,向Contact URI添加";ata=cisco"参数
fix_nated_contact(";ata=cisco");
} else {
否则,仅重写Contact URI为源地址:端口
fix_nated_contact();
}
1
2
3
4
5
6
7
8
fix_nated_contact和fix_nated_register区别
fix_nated_contact :用于修改当前交互中的 Contact 信息
fix_nated_register:用于在注册过程中确保长期的用户位置信息是正确的。
sipmsgops Module
用于在SIP级别上处理消息,包括插入或删除头部、检查方法类型等操作。
is_method
功能:用于检查SIP消息的方法是否与给定的方法名匹配
语法:is_method(method1[|method2|…])
返回值:SIP消息方法是该列表中的任何一个,则函数返回true
Method 描述
invite SIP会话邀请
cancel 取消SIP请求
ack 确认响应
bye 结束SIP会话
options 查询服务器能力
info 会话中传递信息
update 更新会话参数
register 用户注册到SIP服务器
message 通用消息传递
subscribe 订阅事件通知
notify 发送事件通知
refer 呼叫转移请求
prack 临时响应的确认
publish 发布事件状态
if(is_method("INVITE"))
{
…
}
if(is_method("OPTION|UPDATE"))
{
…
}
1
2
3
4
5
6
7
8
has_totag
功能:检查 SIP 消息的 To 头字段中的 URI 是否包含tag参数
存在Tag:该消息属于一个已经建立的会话
语法:has_totag()
返回值:包含,则返回真(true);否则返回假(false)
场景:REQUEST_ROUTE、ONREPLY_ROUTE、FAILURE_ROUTE、BRANCH_ROUTE 和 LOCAL_ROUTE。
检查To头字段的URI是否包含tag参数
if (has_totag()) {
其他操作
xlog("The To header URI contains a tag parameter.\n");
} else {
其他操作
xlog("The To header URI does not contain a tag parameter.\n");
}
1
2
3
4
5
6
7
8
append_hf
功能:用于在SIP消息中添加头字段。
语法:append_hf(txt[, hdr_anchor])
txt (string):要追加的头字段。
hdr_anchor (string):指定在哪个头字段之后追加txt。
注意:主路由中添加的头字段不可在后续路由中删除,应避免添加后续需移除的头字段。可用分支路由临时添加头字段。
场景:REQUEST_ROUTE、ONREPLY_ROUTE、FAILURE_ROUTE、BRANCH_ROUTE、LOCAL_ROUTE
append_hf("P-hint: VOICEMAIL\r\n");
append_hf("From-username: $fU\r\n");
append_hf("From-username: $fU\r\n", "Call-ID");
1
2
3
maxfwd Module
MaX-Forward头域的添加、递减和检查。
mf_process_maxfwd_header
功能:管理 SIP 请求中的 Max-Forwards 头,以防止消息无限转发。
处理流程:
不存在Max-Forward头则添加,初始值为max_value
已存在,会递减其值(除非值为0)
语法:mf_process_maxfwd_header(max_value)
返回值 布尔值 描述
2 true 成功添加新Max-Forwards头
1 true 成功递减Max-Forwards头(非0值)
-1 false Max-Forwards头值为0,无法递减
-2 false 处理过程中出错
返回值通过$retcode或是$rc进行读取
场景: REQUEST_ROUTE
示例
…
initial sanity checks — messages with
max_forwards==0, or excessively long requests
if (!mf_process_maxfwd_header(10) && $retcode==-1) {
sl_send_reply(483,"Too Many Hops");
exit;
};
…
1
2
3
4
5
6
7
8
sl Module
SL模块让OpenSIPS能够无状态地响应SIP请求,并有效管理ACK消息。
sl_send_reply
功能:发送指定状态码和文本原因的SIP 响应。
语法:sl_send_reply(code,"reason")
code (int): 返回码
reason (string): 文本原因(可包含伪变量,运行时会被替换为实际的值,如:$err.rcode 和 $err.rreason)
场景: REQUEST_ROUTE和 ERROR_ROUTE
注意:发送的回复是无状态的,不会触发任何与事务相关的后续操作或重传
…
sl_send_reply(404, "Not found");
…
sl_send_reply($err.rcode, $err.rreason);
…
1
2
3
4
5
signaling Module
封装了tm和sl模块,提供统一的回复发送接口。
如果存在事务,则使用tm模块发送有状态的回复。
如果不存在事务,则使用sl模块发送无状态的回复。
send_reply
语法:send_reply(code, reason)
与上述sl_send_reply大致相同,区别是回复有无状态取决于加载的模块以及是否创建事务。
registrar Module
该模块包含基于RFC 3261的SIP REGISTER请求处理逻辑,并提供了一些扩展功能。
save
功能:save函数用于处理REGISTER消息,根据Contact和Expires头字段来添加、删除或修改用户位置(usrloc)记录。成功时返回200 OK,并列出所有当前保存在usrloc中的联系人;如果出现错误,则发送带有简短原因描述的错误消息。
语法:save(domain[, flags[, aor[, ownership_tag]]]);
domain:逻辑域或数据库表名。
flags:控制行为的标志组合。
aor:自定义AOR,默认从TO头获取。
ownership_tag:标记联系人所属节点。
返回值:成功返回true(或非零值),失败返回false(或0)。
场景:适用于处理SIP注册请求,维护联系人列表。
save("location");
save("location", "memory-only, max-contacts=5");
save("location", "", $fu);
save("location", "memory-only, no-reply", $avp(aor));
save("location", "", "", "vip");
1
2
3
4
5
配置save函数来管理注册请求和用户位置记录。
lookup
功能:lookup函数从usrloc中提取请求URI的用户名,并查找所有相关联系人;如果未找到联系人则返回-1,否则用最高q值的联系人覆盖请求URI,并根据参数决定是否追加其余联系人。
语法:lookup(domain [, flags [, aor]]);
参数 描述
domain 用于查找的表名。
flags 控制查找行为的标志(具体参数查看下表)
aor 要查找的AOR;如果缺失,则使用请求URI中的AOR。
flags标志 描述
no-branches 只覆盖请求URI,不附加分支。
to-branches-only 仅作为分支附加,不更改请求URI。
branch 在现有分支中查找AOR并扩展为联系人。
method-filtering 启用基于注册时允许方法的过滤。
ua-filtering=[val] 按User-Agent正则表达式过滤。
case-insensitive 启用User-Agent过滤时不区分大小写。
extended-regexp 启用User-Agent过滤的扩展正则表达式格式。
global 在分布式环境中执行全局查找。
max-ping-latency=[int] 设置最大接受的ping延迟。
sort-by-latency 按ping延迟升序选择联系人。
返回值 描述
1 找到联系人并成功作为分支推送。
2 启动了异步推送通知但未增加额外分支。
-1 未找到联系人。
-2 找到联系人但都不支持当前SIP方法。
-3 处理期间内部错误。
场景:REQUEST_ROUTE和FAILURE_ROUTE中使用。
lookup("location"); # 简单查找
lookup("location", "method-filtering"); # 启用方法过滤的查找
lookup("location", "branch"); # 启用AOR分支查找;除第一个外的所有联系人将放入分支
lookup("location", "ua-filtering=/phone/i"); # 启用User-Agent过滤的查找
lookup("location", "", $var(aor)); # 使用变量中的AOR进行简单查找
switch ($retcode) {
case -1:
case -3:
sl_send_reply(404, "Not Found");
exit;
case -2:
sl_send_reply(405, "Method Not Allowed");
exit;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ALIAS_DB Module
功能:ALIAS_DB模块用于处理用户别名,作为usrloc的替代方案。
特点:仅使用数据库搜索(不缓存数据),虽可能降低搜索速度但配置更简单,并支持在同一脚本中搜索不同表,使用高速数据库可减少速度损失。
alias_db_lookup
功能:alias_db_lookup函数用于检查R-URI是否为别名,如果是,则将其替换为用户的SIP URI。
语法:alias_db_lookup(table_name, [flags]);
table_name:用于搜索别名的表名。
flags:控制别名查找的标志,包括:
d:仅基于用户名查找(不使用域部分)。
r:反向查找(查找映射到当前URI的别名)。
返回值:
true:如果R-URI是别名且成功替换为用户的SIP URI。
false:如果R-URI不是别名或替换未成功。
场景:此函数可以在REQUEST_ROUTE和FAILURE_ROUTE中使用。
查找并替换别名,反向查找,不使用域部分
alias_db_lookup("dbaliases", "rd");
根据用户名首字母动态构建表名并查找别名
alias_dblookup("dba$(rU{s.substr,0,1})");
1
2
3
4
5
展示使用alias_db_lookup函数进行别名查找和替换。
textops Module
该模块提供了对SIP消息的文本级操作,包括正则表达式搜索替换、Perl风格的替换等功能。
备注:所有与SIP相关的函数,如insert_hf、append_hf及编解码操作,已移至sipmsgops模块。
has_body
功能:has_body函数用于检测SIP消息是否附带了体部(body)。此外,还会检查Content-Length头的存在及其值。
语法:has_body([mime]);
mime:用于与Content-Type头进行比较的MIME类型。如果不提供或为0,则禁用此项检查。
MIME类型定义了数据的内容类型和格式,由主类型和子类型组成,用于标识数据的具体格式。
返回值:
真(true):如果SIP消息包含体部,并且(如果有指定)MIME类型匹配。
假(false):如果SIP消息不包含体部,或者(如果有指定)MIME类型不匹配。
场景:REQUEST_ROUTE、ONREPLY_ROUTE、FAILURE_ROUTE和BRANCH_ROUTE中使用。
if (has_body("application/sdp")) {
do interesting stuff here
}
1
2
3
has_body函数来检查SIP消息是否包含特定MIME类型的体部,并根据结果执行相应操作。
rtpproxy Module
该模块用于OpenSIPS与RTPProxy通信,RTPProxy是一种媒体中继代理,用于使NAT后的用户代理之间能够通信。
该模块还与RTPProxy配合使用,用于录制用户代理之间的媒体流或向用户代理播放媒体。
rtpproxy_offer
功能:rtpproxy_offer函数用于重写SDP体部,确保媒体通过RTP代理传递。应在INVITE请求和200 OK响应中调用。
语法:rtpproxy_offer([[flags][, [ip_address][, [set_id][, [sock_var][, [ret_var][, [body_var]]]]]])
参数 描述
flags 控制行为的标志。
ip_address RTP代理的IP地址。
set_id 用于标识RTP会话的ID。
sock_var 存储套接字信息的变量名。
ret_var 存储返回值的变量名。
body_var 输入输出的体部变量。如果指定,则使用其内容作为挑战RTP代理服务器的体部,并将结果体部返回到该变量中。
场景:REQUEST_ROUTE、ONREPLY_ROUTE、FAILURE_ROUTE和BRANCH_ROUTE
route {
…
if (is_method("INVITE")) {
if (has_body("application/sdp")) {
if (rtpproxy_offer())
t_on_reply("1");
} else {
t_on_reply("2");
}
}
if (is_method("ACK") && has_body("application/sdp"))
rtpproxy_answer();
…
}
onreply_route[1] {
…
if (has_body("application/sdp"))
rtpproxy_answer();
…
}
onreply_route[2] {
…
if (has_body("application/sdp"))
rtpproxy_offer();
…
}
处理INVITE和ACK请求时使用rtpproxy_offer函数来重写SDP体部,并确保媒体通过RTP代理传递。
rtpproxy_answer
功能:rtpproxy_answer函数重写SDP体部,确保媒体通过RTP代理传递。应在200 OK响应和ACK请求中调用。
语法:rtpproxy_answer([[flags][, [ip_address][, [set_id][, [sock_var][, [ret_var][, [body_var]]]]]]);
参数:同rtpproxy_offer()函数的参数。
使用场景:REQUEST_ROUTE、ONREPLY_ROUTE、FAILURE_ROUTE和BRANCH_ROUTE。
参考rtpproxy_offer()函数的示例。
转载请注明:SuperIT » OpenSIPS3.4介绍和安装配置