Kamailio 是一款开源的 SIP 服务器,用于构建大规模实时通信平台。
Kamailio 的预处理器指令(Preprocessor Directives)以单个 # 开头,执行诸如宏定义、文件包含、检查条件是否满足、字符串替换等预处理操作,方便大家写出灵活并且强大的路由脚本。
众所周知,OpenSIPS 自身是不带预处理功能的,需要借助带三方工具(比如 M4)来实现预处理器能力。而 Kamaiilio 在这方面就有优势。
以下是一些常用的 Kamailio 预处理器指令及其功能:
include_file、import_file
include_file 包含文件,该文件必须存在,否则就启动失败。
import_file 也是包含文件,但该文件可以存在,也可以不存在(kamailio 继续运行)。
包含文件的好处主要是可以把路由脚本拆成若干文件,增强可读性,也方便维护。比如我们可以把所有的宏定义组织到一个文件,模块和模块参数放到另外一个文件,请求路由、分支路由、回复路由、失败路由的处理独立成不同的一个文件。
define、ifdef、ifndef、else、endif 等宏指令
define 定义宏,ifdef、ifndef、else、endif 等的用法跟 C 语言的宏指令是很类似。
举例:
试举一个相对比较完整的路由例子:
#!define FLT_NATS 5 # NAT 事务标志
#!define FLB_NATB 6 # NAT 分支标志
#!define FLB_NATSIPPING 7 # NAT SIP PING 分支标志,Kamailio 主动向终端发送 SIP PING 请求
# ...
route[NATDETECT] {
if (nat_uac_test("19")) { # NAT UAC 检查,参数可以是 83(19 + 64),其中 64 检查 UAC Transport 是否为 WS(或者 WSS)
if (is_method("REGISTER")) { # 如果是注册请求
fix_nated_register(); # 修复来自 NAT 的注册请求
} else {
if(is_first_hop()) { # 如果 Kamailio 是边沿 SIP Proxy,不是中间的 SIP Proxy
set_contact_alias(); # 重写 Contact 头
}
}
setflag(FLT_NATS); # 设置 NAT 事务标志
}
return;
}
route[REGISTRAR] {
if (!is_method("REGISTER")) return; # 如果请求的方法不是 REGISTER,那么马上返回
if (isflagset(FLT_NATS)) { # 如果已经设置了 NAT 事务标志
setbflag(FLB_NATB); # 设置 NAT 分支标志
setbflag(FLB_NATSIPPING); # 同时设置 NAT SIP PING 分支标志
}
if (!save("location")) {
sl_reply_error();
}
exit;
}
如果 Kamailio 不支持宏定义,把 FLT_NATS、FLB_NATB 等写成数字,可读性就很差了。
再举一例:
#### 如果需要 SIPTRACE 功能,那么就定义 WITH_SIPTRACE
#### 如果不需要,就不定义这个宏
#!define WITH_SIPTRACE # 定义宏 WITH_SIPTRACE
# 如果连续二个或者更多 #,比如 “##!define WITH_SIPTRACE”,就成了注释
#!ifdef WITH_SIPTRACE # 检查是否定义了 WITH_SIPTRACE 宏
/* heplify-server 地址 */
#!define HEPLIFY_SERVER "sip:127.0.0.1:9060"
#!define FLT_SIPTRACE 22
loadmodule "siptrace.so"
modparam("siptrace", "duplicate_uri", HEPLIFY_SERVER)
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "trace_to_database", 0) # SIP 信令不写数据库
modparam("siptrace", "trace_flag", FLT_SIPTRACE)
modparam("siptrace", "trace_on", 1)
route[SIPTRACE] {
sip_trace_mode("d"); # "d" 的意思是 sip 对话(dialog), 包括了 SIP 请求和 SIP 应答(默认只包括 SIP 请求)
sip_trace();
setflag(FLT_SIPTRACE);
return;
}
#!endif # 结束 “#!ifdef WITH_SIPTRACE”
defenv、defenvs
defenv 定义环境变量
下面是一个能实际运行的完整路由脚本:
debug=2
children=2
fork=no
auto_aliases=no
log_stderror=yes
loadmodule "pv.so"
#!defenv KAM_SIP_PORT=SIP_PORT # 定义宏 KAM_SIP_PORT,其值来自环境变量 SIP_PORT
listen=udp:127.0.0.1:KAM_SIP_PORT
loadmodule "jsonrpcs.so"
loadmodule "sl.so"
loadmodule "ctl.so"
loadmodule "corex.so"
loadmodule "kex.so"
request_route {
sl_send_reply("200", "OK");
exit;
}
先运行 export SIP_PORT=5070
设置环境变量 接着运行 kamailio,控制台输出如下:
0(2280) INFO: <core> [core/sctp_core.c:74]: sctp_core_check_support(): SCTP API not enabled - if you want to use it, load sctp module
Listening on
udp: 127.0.0.1 [127.0.0.1]:5070
Aliases:
WARNING: no fork mode
0(2280) INFO: <core> [core/tcp_main.c:5218]: init_tcp(): using epoll_lt as the io watch method (auto detected)
...
defenvs 的作用跟 defenv 类似,但前者多了双引号
我们在刚才那个脚本的基础上增加一行,全部内容为:
debug=2
children=2
fork=no
auto_aliases=no
log_stderror=yes
loadmodule "pv.so"
#!defenv KAM_SIP_PORT=SIP_PORT # 定义宏 KAM_SIP_PORT,其值来自环境变量 SIP_PORT
#!defenvs KAM_SIP_DOMAIN=SIP_DOMAIN # 定义宏 KAM_SIP_DOMAIN,其值来自环境变量 SIP_DOMAIN
listen=udp:127.0.0.1:KAM_SIP_PORT
loadmodule "jsonrpcs.so"
loadmodule "sl.so"
loadmodule "ctl.so"
loadmodule "corex.so"
loadmodule "kex.so"
request_route {
sl_send_reply("200", "OK");
exit;
}
先运行 export SIP_PORT=5070 && export SIP_DOMAIN=kamailio.org
设置环境变量
再运行 kamailio,等 kamailio 成功启动之后,再运行 kamcmd core.ppdefines_full
,输出内容为(节选):
{
name: KAM_SIP_PORT
type: 0
value: 5070
}
{
name: KAM_SIP_DOMAIN
type: 0
value: "kamailio.org" # 多了双引号
}
subst 字符串替换。
下面是成功的例子:
下面的例子单个 subst 没问题,但放一起就会出问题:
#!subst "/SIP_PORT/5060/g"
#!subst "/SIP_PORT_2/5080/g" # 处理成 /5060_2/5080/g,subst 执行的是字符串替换
substdef 的作用跟 subst 是一样的,用 kamcmd core.ppdefines_full 去观察,subst 是没有 ID 定义的,而 substdef 则有 ID 定义(KEMI 用 KX 模块可以访问这个 ID)。
substdefs 比 substdef 多双引号。
define 和 substdef 组合
请看下面这个例子:
#!substdef "!MYSQL_USERNAME!kamailio!g"
#!substdef "!MYSQL_PASSWORD!1234@abde!g"
#!substdef "!MYSQL_HOST!127.0.0.1!g"
#!substdef "!MYSQL_PORT!3306!g"
#!substdef "!MYSQL_DBNAME!kamaiilio!g"
#!define MYSQL_DBURL "mysql://MYSQL_USERNAME:MYSQL_PASSWORD@MYSQL_HOST:MYSQL_PORT/MYSQL_DBNAME"
modparam("auth_db", "db_url", MYSQL_DBURL)
如果数据库密码里面有 “@”,或者别的特殊符号,需要把 define 和 substdef 组合起来使用。
defexp、ifexp、defexps
defexp、ifexp 等是 2022 年 9 月新增加的预处理指令。
下面是一个简单的例子:
#!ifexp KAMAILIO_VERSION >= 5006000 # `kamcmd core.ppdefines_full` 能查
...
#!else
...
#!endif
下面例子稍微复杂一点(很实用):
loadmodule "htable.so"
...
#!ifexp Mod_htable
#!else
loadmodule "htable.so" # 如果 htable 模块尚未加载,现在就加载 htalbe
#!endif
可以用上面的技巧避免某个模块重复装载。
更详细的说明可以参考下面链接:
本文分享自 FreeSWITCH中文社区 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有