首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >kamailio NAT模块和媒体处理细节说明

kamailio NAT模块和媒体处理细节说明

作者头像
杜金房
发布2026-07-23 15:12:49
发布2026-07-23 15:12:49
480
举报

kamailio NAT模块是公网NAT透传的核心模块,使用kamalio做公网的终端外网注册,SIP trunk对接,和SBC实现多种业务场景时,我们都需要启用这个模块。在很多历史文档中,和在售的电子书也对此模块配置使用有非常深入讲解。今天,我们重走长征路,从一个角度更深层次的代码级的角度来学习Kamailio。

NAT穿透与媒体

nathelpersdpops模块提供了在用户代理(UA)位于网络地址转换(NAT)后面的环境中处理 SIP 信令和会话描述协议(SDP)载荷的核心逻辑。这些模块使 Kamailio 能够检测 NAT 后的端点、修复联系信息、通过保活机制维持 NAT 绑定,并操控媒体参数以确保音视频流的正常传输。


NAT 检测与修正

nathelper模块通过比较 IP 数据包的源 IP/端口与 SIP 头部及 SDP 消息体中通告的地址来识别 NAT 后的客户端。

NAT 识别逻辑

nat_uac_test(flags)函数实现了多项测试来判断 UA 是否位于 NAT 后面:

  • 0x01 (C_1918):检查 Contact头部中的 IP 是否为 RFC1918 私有地址。
  • 0x02 (RCVD):将请求的源 IP 与 Via头部中的 IP 进行比较。
  • 0x10 (RPORT):检查源端口是否与 Via头部中的端口不同。

头部操控

一旦检测到 NAT,可使用以下函数对信令进行"修复":

  • fix_nated_contact():用数据包的实际源 IP 和端口覆盖 Contact头部。
  • add_contact_alias():在 ContactURI 中追加包含源地址的参数。这是 fix_nated_contact的符合标准的替代方案,在保留原始 URI 的同时允许代理路由回源地址。
  • fix_nated_register():将源地址提取到 AVP(属性值对)中,以便在注册时由 usrloc模块存储。

NAT 穿透数据流

下图展示了 REGISTER请求期间 NAT 后的 UA 与 nathelper模块之间的交互。

NAT 穿透逻辑流程

"usrloc 模块""Kamailio (nathelper)""NAT 后的 UA""usrloc 模块""Kamailio (nathelper)""NAT 后的 UA"nat_uac_test(0x01) 返回 True源 IP 提取至 $avp(received)存储 Contact + 接收到的 IPSIP REGISTER (Contact: 192.168.1.10)fix_nated_register()save("location")200 OK


NAT保活工作机制(Pinging)

为防止 NAT 绑定过期,nathelper会定期向已注册的 UA 发送"ping"。

Ping 机制

Kamailio 支持两种主要的保活类型:

  1. 1. UDP Ping:发送 4 字节全零填充的 UDP 数据包。轻量级,但仅提供单向流量。
  2. 2. SIP Ping(OPTIONS):发送无状态 SIP OPTIONS请求。这会强制 UA 响应,产生双向流量,从而可靠地维持 NAT 绑定。

实现组件

Ping 逻辑由专用定时器进程管理,通过 natping_processes进行配置。 这些进程遍历 usrloc数据库,并通过 sip_pinger.h发送数据包。

NAT Pinger 组件映射

usrloc 模块

nathelper 模块

Kamailio 核心

定时器进程 (pt.c)

nh_timer()

sip_pinger.h / send_ping()

ul_entries (共享内存)

网络套接字


SDP 管理控制(sdpops)

sdpops模块提供了检查和修改 SIP 消息 SDP 消息体的高级能力。nathelper通过 fix_nated_sdp()执行基本的 SDP IP 修复,而 sdpops则允许对媒体流进行精细控制。

主要函数

  • sdp_remove_codecs_by_id(codecs, media):从媒体流中删除指定的载荷类型(例如删除 PCMU)。
  • sdp_keep_codecs_by_name(codecs, media):删除除指定名称之外的所有编解码器(例如保留 "G729")。
  • sdp_with_media(type):检查 SDP 是否包含特定媒体类型(如 "video" 或 "application")。
  • sdp_print(level):记录已解析的 SDP 结构以供调试。

数据结构:sdp_info

该模块依赖核心 SDP 解析器,后者会填充 sdp_info结构体。sdpops使用该结构体在 SIP 消息缓冲区中定位特定行,并通过 data_lump系统应用修改,确保在消息转发时安全地执行变更。

SDP 操控架构

sdpops 模块

SIP 消息

sip_msg_t

SDP 消息体

core/parser/sdp/sdp.c

w_sdp_remove_codecs_by_id()

core/data_lump.h


配置参数

nathelper 参数

参数

类型

说明

natping_interval

整数

NAT ping 之间的间隔秒数,0 表示禁用 ping。

ping_nated_only

整数

若为 1,则仅对 usrloc 中带有 NAT 标志的联系人发送 ping。

sipping_method

字符串

用于保活的 SIP 方法(默认:OPTIONS)。

force_socket

字符串

指定发送 ping 时使用的本地套接字。 25

sdpops 参数

sdpops模块主要向路由脚本导出函数,而非配置参数,因为其行为通常是按呼叫动态决定的。

Citations

代码File:src/modules/nathelper/nathelper.c (L85-85)

代码语言:javascript
复制
#include "sip_pinger.h"

代码File:src/modules/nathelper/nathelper.c (L97-107)

代码语言:javascript
复制
#define NAT_UAC_TEST_C_1918 0x01
#define NAT_UAC_TEST_RCVD 0x02
#define NAT_UAC_TEST_V_1918 0x04
#define NAT_UAC_TEST_S_1918 0x08
#define NAT_UAC_TEST_RPORT 0x10
#define NAT_UAC_TEST_O_1918 0x20
#define NAT_UAC_TEST_WS 0x40
#define NAT_UAC_TEST_C_PORT 0x80
#define NAT_UAC_TEST_SDP_CLINE 0x100
#define NAT_UAC_TEST_DEST 0x200

代码File:src/modules/nathelper/nathelper.c (L111-135)

代码语言:javascript
复制
static int nat_uac_test_f(struct sip_msg *msg, char *str1, char *str2);
static int fix_nated_contact_f(struct sip_msg *, char *, char *);
static int add_contact_alias_0_f(struct sip_msg *, char *, char *);
static int add_contact_alias_3_f(struct sip_msg *, char *, char *, char *);
static int set_contact_alias_f(struct sip_msg *msg, char *str1, char *str2);
static int w_set_contact_alias_f(struct sip_msg *msg, char *str1, char *str2);
static int handle_ruri_alias_f(struct sip_msg *, char *, char *);
static int handle_ruri_alias_mode_f(sip_msg_t *msg, char *pmode, char *p2);
static int pv_get_rr_count_f(struct sip_msg *, pv_param_t *, pv_value_t *);
static int pv_get_rr_top_count_f(struct sip_msg *, pv_param_t *, pv_value_t *);
static int fix_nated_sdp_f(struct sip_msg *, char *, char *);
static int is_rfc1918_f(struct sip_msg *, char *, char *);
static int nh_extract_mediaip(str *, str *, int *, char *, int);
static int alter_mediaip(struct sip_msg *, str *, str *, int, str *, int, int);
static int fix_nated_register_f(struct sip_msg *, char *, char *);
static int fixup_fix_nated_register(void **param, int param_no);
static int fixup_fix_sdp(void **param, int param_no);
static int fixup_free_fix_sdp(void **param, int param_no);
static int fixup_add_contact_alias(void **param, int param_no);
static int fixup_free_add_contact_alias(void **param, int param_no);
static int add_rcv_param_f(struct sip_msg *, char *, char *);
static int nh_sip_reply_received(sip_msg_t *msg);
static int test_sdp_cline(struct sip_msg *msg);

static int w_set_alias_to_pv(struct sip_msg *msg, char *uri_avp, char *hollow);

代码File:src/modules/nathelper/nathelper.c (L140-210)

代码语言:javascript
复制
static void nh_timer(unsigned int, void *);
static int mod_init(void);
static int child_init(int);
static void mod_destroy(void);

static int nathelper_rpc_init(void);

static usrloc_api_t ul;

static int cblen =;
static int natping_interval =;
struct socket_info *force_socket =;

static int nh_nat_addr_mode =;

/* clang-format off */
typedef struct nh_netaddr {
    const char *cnetaddr;
    uint32_t netaddr;
    uint32_t mask;
} nh_netaddr_t;

static nh_netaddr_t nh_nets_1918[] = {
    {"10.0.0.0",, 0xffffffffu <<},
    {"172.16.0.0",, 0xffffffffu <<},
    {"192.168.0.0",, 0xffffffffu <<},
    {"100.64.0.0",, 0xffffffffu <<}, /* rfc6598 - cg-nat */
    {"192.0.0.0",, 0xffffffffu <<}, /* rfc7335 - IPv4 Service Continuity Prefix */
    {NULL,,}
};

static nh_netaddr_t nh_nets_extra[] = {
    {"192.0.0.0",, 0xffffffffu <<}, /* rfc7335 - IETF Protocol Assignments */
    {NULL,,}
};
/* clang-format on */

/*
 * If this parameter is set then the natpinger will ping only contacts
 * that have the NAT flag set in user location database
 */
static int ping_nated_only =;
static const char sbuf[] = {,,,};
static str force_socket_str = STR_NULL;
static int sipping_flag = -1;
static int natping_disable_flag = -1;
static int natping_processes =;

static str nortpproxy_str = str_init("a=nortpproxy:yes");

static char *rcv_avp_param = NULL;
static avp_flags_t rcv_avp_type =;
static avp_name_t rcv_avp_name;

static char *natping_socket = NULL;
static int udpping_from_path =;
static int ignore_path =;
static int sdp_oldmediaip =;
static int raw_sock = -1;
static unsigned int raw_ip =;
static unsigned short raw_port =;
static int nh_keepalive_timeout =;
static request_method_t sipping_method_id =;
/* filter contacts by server_id */
static int nh_filter_srvid =;

/*0-> disabled, 1 ->enabled*/
unsigned int *natping_state = NULL;

static str nh_alias_name = str_init("");
代码语言:javascript
复制


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeSWITCH中文社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • NAT穿透与媒体
    • NAT 检测与修正
      • NAT 识别逻辑
      • 头部操控
      • NAT 穿透数据流
    • NAT保活工作机制(Pinging)
      • Ping 机制
      • 实现组件
    • SDP 管理控制(sdpops)
      • 主要函数
      • 数据结构:sdp_info
    • 配置参数
      • nathelper 参数
      • sdpops 参数
      • Citations
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档