Featured image of post topology_hiding模块介绍

topology_hiding模块介绍

功能介绍

topology_hiding 功能主要是隐藏Hearder头里的(Via,Record-Route,Route),防止被其他服务器探测到。

在opensips以udp方式转发sip请求时,有可能Header很大,超过了MTU,此时udp会有概率发送失败。 有人可能会想用此方式来减少包量,通过这篇介绍,你就能知道实际上Header头并未减少多少,效果不大。 遇到这种情况,还不如删除一部分头或者改成tcp方式。

模块配置

opensips版本:

opensips 3.3.10 (x86_64/linux)

  1. 正常代理的配置:
  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)");
}
  1. 使用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来解析链路转发。

实际使用示例

完整通话信令图: call

INVITE到被代理方(114机器)

正常sip

正常invite

隐藏sip

隐藏invite

可以看到Record-Route被隐藏了, Via隐藏了软电话的地址。

Contact替换成了发送到被代理方请求出口的ip和port。 Contact内隐藏的一些其他信息按照base64方式存到了customparam, 可以看到数据还是很长。

被代理方返回200OK

因为183和200ok的头差别不大,所以以200OK为例:

正常sip

正常200ok

隐藏sip

隐藏200ok

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

opensips返回200OK

正常sip

正常200ok

隐藏sip

隐藏200ok

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

软电话返回ACK

正常sip

正常ack

隐藏sip

隐藏ack

在使用topology_hiding时, 因为返回的200OK,没有Record-Route,所以软电话返回的ACK也就没有Route

被代理方返回BYE

正常sip

正常bye

隐藏sip

隐藏bye 在使用topology_hiding时, 因为收到的INVITE,没有Record-Route,所以被代理方发送BYE也就没有Record-RouteVia使用的是INVITE带的Via

opensips返回BYE

正常sip

正常bye

隐藏sip

隐藏bye

在使用topology_hiding时, 修改了Via为出口ip和port.

软电话返回200OK

正常sip

正常200ok

隐藏sip

隐藏200ok

opensips返回200OK

正常sip

正常200ok

隐藏sip

隐藏200ok 在使用topology_hiding时, 修改了Via为被代理方的ip和port.

topology_hiding 参数示例

U:用户名补充到contact中

1
topology_hiding("U");

往被代理方发送INVITE,效果: U参数 topology_hiding("U")会补充用户名1002到contact中

D:把DID补充到Contact用户名中

1
2
modparam("topology_hiding", "force_dialog", 1)
topology_hiding("D");

往被代理方发送INVITE,效果: D参数

C:对Call-id编码

1
2
modparam("topology_hiding", "force_dialog", 1)
topology_hiding("C");

往被代理方发送INVITE,效果: D参数

call-id会被编码,导致和软电话请求过来的call-id不一致。

a/A:

目前并未看到效果.

本博客已稳定运行
发表了26篇文章 · 总计45.09k字
本站总访问量 次 · 您是本站第 位访问者
粤ICP备2025368587号-1| 使用 Hugo 构建
主题 StackJimmy 设计