功能介绍
topology_hiding 功能主要是隐藏Hearder头里的(Via
,Record-Route
,Route
),防止被其他服务器探测到。
在opensips以udp方式转发sip请求时,有可能Header很大,超过了MTU
,此时udp会有概率发送失败。
有人可能会想用此方式来减少包量,通过这篇介绍,你就能知道实际上Header
头并未减少多少,效果不大。
遇到这种情况,还不如删除一部分头或者改成tcp方式。
模块配置
opensips版本:
opensips 3.3.10 (x86_64/linux)
- 正常代理的配置:
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
loadmodule "dialog.so"
route{
if (!mf_process_maxfwd_header(10)) {
sl_send_reply(483,"Too Many Hops");
exit;
}
if (has_totag()) {
# sequential requests within a dialog should
# take the path determined by record-routing
xlog("L_DBG","[$cfg_line][$ci]---has_totag-:$rm|$rs|$tu|$socket_in(port)\n");
if (loose_route()) {
if (is_method("INVITE")) {
# even if in most of the cases is useless, do RR for
# re-INVITEs alos, as some buggy clients do change route set
# during the dialog.
record_route();
}
# route it out to whatever destination was set by loose_route()
# in $du (destination URI).
xlog("L_DBG","[$cfg_line][$ci]---is-loose_route-:$rm|$rs|$tu|$socket_in(port)\n");
route(relay);
} else {
xlog("L_DBG","[$cfg_line][$ci]---not-loose_route-:$rm|$rs|$tu|$socket_in(port)\n");
if ( is_method("ACK") ) {
if ( t_check_trans() ) {
# non loose-route, but stateful ACK; must be an ACK after
# a 487 or e.g. 404 from upstream server
t_relay();
exit;
} else {
# ACK without matching transaction ->
# ignore and discard
exit;
}
}
sl_send_reply(404,"Not here");
}
exit;
}
# CANCEL processing
if (is_method("CANCEL")) {
if (t_check_trans())
t_relay();
exit;
}
t_check_trans();
#if (!is_method("REGISTER")) {
# if (is_myself("$fd")) {
# # if caller is not local, then called number must be local
# if (!is_myself("$rd")) {
# send_reply(403,"Rely forbidden");
# exit;
# }
# }
#}
# preloaded route checking
if (loose_route()) {
xlog("L_ERR",
"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
if (!is_method("ACK"))
sl_send_reply(403,"Preload Route denied");
exit;
}
# record routing
if (!is_method("REGISTER|MESSAGE"))
record_route();
if (!is_myself("$rd")) {
append_hf("P-hint: outbound\r\n");
route(relay);
}
# requests for my domain
if (is_method("PUBLISH|SUBSCRIBE")) {
sl_send_reply(503, "Service Unavailable");
exit;
}
# check if the clients are using WebSockets or WebSocketSecure
if ( $socket_in(proto) == "WS"|| $socket_in(proto) == "WSS")
setflag("SRC_WS");
# consider the client is behind NAT - always fix the contact
#fix_nated_contact();
xlog("L_DBG","[$cfg_line][$ci]---main-1-:$rm|$rs|$tu|$socket_in(port)|$hdr(contact)\n");
if (is_method("REGISTER")) {
# indicate that the client supports DTLS
# so we know when he is called
if (isflagset("SRC_WS"))
setbflag("DST_WS");
fix_nated_register();
if (!save("location"))
sl_reply_error();
exit;
}
xlog("L_DBG","[$cfg_line][$ci]---main--:$rm|$rs|$tu|$socket_in(port)|$hdr(contact)\n");
if ($rU==NULL) {
# request with no Username in RURI
sl_send_reply(484,"Address Incomplete");
exit;
}
# do lookup with method filtering
if (!lookup("location","m")) {
$ru = "sip:172.16.4.114:5080";
$socket_out = "udp:172.16.4.111:5361";
}
#if (!lookup("location","m")) {
# t_newtran();
# t_reply(404, "Not Found");
# exit;
#}
route(relay);
}
route[relay] {
# for INVITEs enable some additional helper routes
if (is_method("INVITE")) {
t_on_branch("handle_nat");
t_on_reply("handle_nat");
} else if (is_method("BYE|CANCEL")) {
rtpengine_delete();
}
if (!t_relay()) {
send_reply(500,"Internal Error");
};
exit;
}
branch_route[handle_nat] {
if (!is_method("INVITE") || !has_body("application/sdp"))
return;
$var(rtp_flag) = "replace-origin ";
if (isflagset("SRC_WS") && isbflagset("DST_WS")) { #web->web
$var(rtp_flag) = $var(rtp_flag) + " ICE=force-relay DTLS=passive";
} else if (isflagset("SRC_WS") && !isbflagset("DST_WS")){ #web->sip
$var(rtp_flag) = $var(rtp_flag) + " codec-strip-G722 codec-strip-CN codec-strip-red strip-extmap rtcp-mux-demux DTLS=off SDES-off ICE=remove RTP/AVP";
} else if (!isflagset("SRC_WS") && isbflagset("DST_WS")) {#sip->web
$var(rtp_flag) = $var(rtp_flag) + " rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=force RTP/SAVPF";
} else if (!isflagset("SRC_WS") && !isbflagset("DST_WS")) #sip->sip
$var(rtp_flag) = $var(rtp_flag) + " DTLS=off SDES-off ICE=remove RTP/AVP";
rtpengine_offer("$var(rtp_flag)");
}
onreply_route[handle_nat] {
fix_nated_contact();
if (!has_body("application/sdp"))
return;
$var(rtp_flag) = "replace-origin ";
if (isflagset("SRC_WS") && isbflagset("DST_WS")) #web->web
$var(rtp_flag) = $var(rtp_flag) + " ICE=force-relay DTLS=passive";
else if (isflagset("SRC_WS") && !isbflagset("DST_WS")) #web->sip
$var(rtp_flag) = $var(rtp_flag) + " codec-strip-G722 codec-strip-CN codec-strip-red codec-strip-opus rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=force";
else if (!isflagset("SRC_WS") && isbflagset("DST_WS")) #sip->web
$var(rtp_flag) = $var(rtp_flag) + " rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=remove RTP/AVP";
else if (!isflagset("SRC_WS") && !isbflagset("DST_WS")) #sip->sip
$var(rtp_flag) = $var(rtp_flag) + " DTLS=off SDES-off ICE=remove RTP/AVP";
rtpengine_answer("$var(rtp_flag)");
}
|
- 使用topology_hiding的配置:
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
loadmodule "dialog.so"
loadmodule "topology_hiding.so"
modparam("topology_hiding", "th_callid_passwd", "my_topo_hiding_secret")
modparam("topology_hiding", "th_callid_prefix", "MYCALLIDPREFIX_")
modparam("topology_hiding", "th_passed_contact_uri_params", "paramname1;myparam;custom_param")
modparam("topology_hiding", "th_passed_contact_params", "paramname1;myparam;custom_param")
modparam("topology_hiding", "th_contact_encode_passwd", "my_topoh_passwd")
modparam("topology_hiding", "th_contact_encode_param", "customparam")
route{
if (!mf_process_maxfwd_header(10)) {
sl_send_reply(483,"Too Many Hops");
exit;
}
if (has_totag()) {
# sequential requests within a dialog should
# take the path determined by record-routing
xlog("L_DBG","[$cfg_line][$ci]---has_totag-:$rm|$rs|$tu|$socket_in(port)\n");
if (topology_hiding_match()) {
if (is_method("INVITE")) {
# even if in most of the cases is useless, do RR for
# re-INVITEs alos, as some buggy clients do change route set
# during the dialog.
record_route();
}
# route it out to whatever destination was set by loose_route()
# in $du (destination URI).
xlog("L_DBG","[$cfg_line][$ci]---is-loose_route-:$rm|$rs|$tu|$socket_in(port)\n");
route(relay);
} else {
xlog("L_DBG","[$cfg_line][$ci]---not-loose_route-:$rm|$rs|$tu|$socket_in(port)\n");
if ( is_method("ACK") ) {
if ( t_check_trans() ) {
# non loose-route, but stateful ACK; must be an ACK after
# a 487 or e.g. 404 from upstream server
t_relay();
exit;
} else {
# ACK without matching transaction ->
# ignore and discard
exit;
}
}
sl_send_reply(404,"Not here");
}
exit;
}
# CANCEL processing
if (is_method("CANCEL")) {
if (t_check_trans())
t_relay();
exit;
}
t_check_trans();
#if (!is_method("REGISTER")) {
# if (is_myself("$fd")) {
# # if caller is not local, then called number must be local
# if (!is_myself("$rd")) {
# send_reply(403,"Rely forbidden");
# exit;
# }
# }
#}
# preloaded route checking
if (loose_route()) {
xlog("L_ERR",
"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
if (!is_method("ACK"))
sl_send_reply(403,"Preload Route denied");
exit;
}
# record routing
if (!is_method("REGISTER|MESSAGE"))
record_route();
if (!is_myself("$rd")) {
append_hf("P-hint: outbound\r\n");
route(relay);
}
# requests for my domain
if (is_method("PUBLISH|SUBSCRIBE")) {
sl_send_reply(503, "Service Unavailable");
exit;
}
# check if the clients are using WebSockets or WebSocketSecure
if ( $socket_in(proto) == "WS"|| $socket_in(proto) == "WSS")
setflag("SRC_WS");
# consider the client is behind NAT - always fix the contact
#fix_nated_contact();
xlog("L_DBG","[$cfg_line][$ci]---main-1-:$rm|$rs|$tu|$socket_in(port)|$hdr(contact)\n");
if (is_method("REGISTER")) {
# indicate that the client supports DTLS
# so we know when he is called
if (isflagset("SRC_WS"))
setbflag("DST_WS");
fix_nated_register();
if (!save("location"))
sl_reply_error();
exit;
}
xlog("L_DBG","[$cfg_line][$ci]---main--:$rm|$rs|$tu|$socket_in(port)|$hdr(contact)\n");
if ($rU==NULL) {
# request with no Username in RURI
sl_send_reply(484,"Address Incomplete");
exit;
}
if (is_method("INVITE") && !has_totag()) {
topology_hiding();
}
# do lookup with method filtering
if (!lookup("location","m")) {
$ru = "sip:172.16.4.114:5080";
$socket_out = "udp:172.16.4.111:5361";
}
#if (!lookup("location","m")) {
# t_newtran();
# t_reply(404, "Not Found");
# exit;
#}
route(relay);
}
route[relay] {
# for INVITEs enable some additional helper routes
if (is_method("INVITE")) {
t_on_branch("handle_nat");
t_on_reply("handle_nat");
} else if (is_method("BYE|CANCEL")) {
rtpengine_delete();
}
if (!t_relay()) {
send_reply(500,"Internal Error");
};
exit;
}
branch_route[handle_nat] {
if (!is_method("INVITE") || !has_body("application/sdp"))
return;
$var(rtp_flag) = "replace-origin ";
if (isflagset("SRC_WS") && isbflagset("DST_WS")) { #web->web
$var(rtp_flag) = $var(rtp_flag) + " ICE=force-relay DTLS=passive";
} else if (isflagset("SRC_WS") && !isbflagset("DST_WS")){ #web->sip
$var(rtp_flag) = $var(rtp_flag) + " codec-strip-G722 codec-strip-CN codec-strip-red strip-extmap rtcp-mux-demux DTLS=off SDES-off ICE=remove RTP/AVP";
} else if (!isflagset("SRC_WS") && isbflagset("DST_WS")) {#sip->web
$var(rtp_flag) = $var(rtp_flag) + " rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=force RTP/SAVPF";
} else if (!isflagset("SRC_WS") && !isbflagset("DST_WS")) #sip->sip
$var(rtp_flag) = $var(rtp_flag) + " DTLS=off SDES-off ICE=remove RTP/AVP";
rtpengine_offer("$var(rtp_flag)");
}
onreply_route[handle_nat] {
fix_nated_contact();
if (!has_body("application/sdp"))
return;
$var(rtp_flag) = "replace-origin ";
if (isflagset("SRC_WS") && isbflagset("DST_WS")) #web->web
$var(rtp_flag) = $var(rtp_flag) + " ICE=force-relay DTLS=passive";
else if (isflagset("SRC_WS") && !isbflagset("DST_WS")) #web->sip
$var(rtp_flag) = $var(rtp_flag) + " codec-strip-G722 codec-strip-CN codec-strip-red codec-strip-opus rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=force";
else if (!isflagset("SRC_WS") && isbflagset("DST_WS")) #sip->web
$var(rtp_flag) = $var(rtp_flag) + " rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=remove RTP/AVP";
else if (!isflagset("SRC_WS") && !isbflagset("DST_WS")) #sip->sip
$var(rtp_flag) = $var(rtp_flag) + " DTLS=off SDES-off ICE=remove RTP/AVP";
rtpengine_answer("$var(rtp_flag)");
}
|
对比两份配置,可以看出在has_totag内的处理有些差异,也就是软电话收到200OK
,返回ACK
之后的处理,
使用了topology_hiding
,软电话返回的ACK
里没有Route
,所以就不需要使用loose_route
,
直接使用topology_hiding_match
来解析链路转发。
实际使用示例
完整通话信令图:

INVITE到被代理方(114机器)
正常sip

隐藏sip

可以看到Record-Route
被隐藏了, Via
隐藏了软电话的地址。
Contact
替换成了发送到被代理方请求出口的ip和port。
Contact
内隐藏的一些其他信息按照base64方式存到了customparam
, 可以看到数据还是很长。
被代理方返回200OK
因为183和200ok的头差别不大,所以以200OK为例:
正常sip

隐藏sip

在使用topology_hiding
时,被代理方因为收到的INVITE
信令中没有Record-Route
,所以返回200OK
也不会有。
opensips返回200OK
正常sip

隐藏sip

在使用topology_hiding
时,opensips把软电话的地址补充到了Via
,
Contact
修改成了opensip出口的ip和port.
软电话返回ACK
正常sip

隐藏sip

在使用topology_hiding
时, 因为返回的200OK
,没有Record-Route
,所以软电话返回的ACK
也就没有Route
。
被代理方返回BYE
正常sip

隐藏sip
在使用topology_hiding
时, 因为收到的INVITE
,没有Record-Route
,所以被代理方发送BYE
也就没有Record-Route
。
Via
使用的是INVITE
带的Via
。
opensips返回BYE
正常sip

隐藏sip

在使用topology_hiding
时, 修改了Via
为出口ip和port.
软电话返回200OK
正常sip

隐藏sip

opensips返回200OK
正常sip

隐藏sip
在使用topology_hiding
时, 修改了Via
为被代理方的ip和port.
topology_hiding 参数示例
往被代理方发送INVITE
,效果:
topology_hiding("U")
会补充用户名1002
到contact中
1
2
|
modparam("topology_hiding", "force_dialog", 1)
topology_hiding("D");
|
往被代理方发送INVITE
,效果:

C:对Call-id编码
1
2
|
modparam("topology_hiding", "force_dialog", 1)
topology_hiding("C");
|
往被代理方发送INVITE
,效果:

call-id会被编码,导致和软电话请求过来的call-id不一致。
a/A:
目前并未看到效果.