Friday, August 29, 2014

Звонок с сайта. Таки доделали.

Вот примерно такой сервис сделали....
Сейчас звонит только на мою голосовую почту, не так много у меня денег, чтобы напрямую на мобильный мне звонило, а SIP я с собой не таскаю постоянно :)
Можете оставлять сообщения, они мне на почту будут приходить :)

позвонить

Monday, June 23, 2014

Примитивный L7 DPI на iptables

Оказывается, начиная с ядра 2.6.14 в Debian (Ubuntu) iptables умеет анализировать пакет текстово с помощью модуля string.
Поэтому можно сделать некий примитивный DPI. У меня есть задача не пропускать на сервер только пакеты SIP REGISTER и INVITE (INVITE допускается только с определенных адресов). Поэтому родилось что-то типа

iptables -A INPUT -m string --string "INVITE sip:" --algo bm -i eth0 -s X.X.X.X/255.255.255.255 -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -m string --string "INVITE sip:" --algo bm -i eth0 -p udp --dport 5060 -j DROP
iptables -A INPUT -m string --string "INVITE sip:" --algo bm -i eth0 -p tcp --dport 5060 -j DROP
iptables -A INPUT -m string --string "REGISTER sip:" --algo bm -i eth0 -p udp --dport 5060 -j DROP
iptables -A INPUT -m string --string "REGISTER sip:" --algo bm -i eth0 -p tcp --dport 5060 -j DROP



По мотивам: http://www.linuxquestions.org/questions/linux-networking-3/iptables-rules-against-udp-flood-and-ddos-attack-789950/

Wednesday, April 2, 2014

Попытки сделать полноценный SIP SBC или OpenSIPS в руках....

Итак.... История не стоит на месте и я тоже. Выпала задачка сделать масштабируемый и (возможно в будущем) высоконагруженный SBC для всяческих сервисов.
Схема, к которой я пришел не отличается оптимизированностью, зато довольно проста в масштабировании (я надеюсь) и эксплуатации.
Схема:

WAN <->  OpenSIPS (1.8) - - Asterisk (any)
                          |                 |
WAN <-> FreeSwitch (1.2) - ---------

Роли:
OpenSIPS - регистрация пользователей, регистрация на внешних сервисах, балансировка между FreeSwitch и Asterisk
FreeSwitch - Meдиа-прокси, при необходимости - транскодинг, скрытие топологии. Количество ограничено внешними IP-адресами.
Asterisk - Собственно, сервер приложений типа голосовой почты и прочего. Количество - в разумных пределах :). Почему, собственно, Asterisk? Потому что человек, который пишет под него приложения, знает AGI.
Схема работы примерно такая:
Пользователь набирает какой-то номер -> OpenSIPS проверяет авторизации и прочее и перенаправляет звонок по WAN на один из FreeSwith'ей -> FreeSwitch просто делает "заворот" WAN - LAN и отправляет звонок обратно на OpenSIPS - > OpenSIPS, видя, что звонок пришел от одного из FreeSwith'ей, балансирует его на один из Астерисков.

Собственно, сигнализация при таком звонке ходит так:
Пользователь (NAT/WAN) - (WAN) OpenSIPS (WAN) - (WAN) FreeSwitch (LAN) - (LAN) OpenSIPS (LAN) - (LAN) Asterisk.
Медиа же ходит так:
Пользователь (NAT/WAN) - (WAN) FreeSwitch (LAN) - (LAN) Asterisk.
 Т.е. в минимальной комплектации нужно 2 внешних IP адреса.

Далее пойдет файлик opensips.cfg с некоторыми комментариями:

#
# $Id: opensips.cfg 8758 2012-02-29 11:59:26Z vladut-paiu $
#
# OpenSIPS residential configuration script
#     by OpenSIPS Solutions
#
# This script was generated via "make menuconfig", from
#   the "Residential" scenario.
# You can enable / disable more features / functionalities by
#   re-generating the scenario with different options.#
#
# Please refer to the Core CookBook at:
#      http://www.opensips.org/Resources/DocsCookbooks
# for a explanation of possible statements, functions and parameters.
#


####### Global Parameters #########

debug=3
log_stderror=no
log_facility=LOG_LOCAL7

fork=yes
children=4

auto_aliases=yes

listen=udp:172.16.3.8:5060
listen=udp:XXX:5060
listen=udp:XXX:5060

#port=5060
disable_tcp=yes

####### Modules Section ########

#set module path
mpath="/usr/lib/opensips/modules/"


#### SIGNALING module
loadmodule "signaling.so"

#### StateLess module
loadmodule "sl.so"

#### MySQL module
loadmodule "db_mysql.so"


loadmodule "regex.so"
loadmodule "textops.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "domain.so"
loadmodule "alias_db.so"

#### Transaction Module
loadmodule "tm.so"
modparam("tm", "fr_timeout", 5)
modparam("tm", "fr_inv_timeout", 30)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "onreply_avp_mode", 1)


#### Record Route Module
loadmodule "rr.so"
modparam("rr", "append_fromtag", 1)

#### MAX ForWarD module
loadmodule "maxfwd.so"

#### SIP MSG OPerationS module
loadmodule "sipmsgops.so"

#### FIFO Management Interface
loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)

#### URI module
loadmodule "uri.so"
modparam("uri", "use_uri_table", 0)

#### USeR LOCation module
loadmodule "usrloc.so"
modparam("usrloc", "nat_bflag", "NAT_BFLAG")
modparam("usrloc", "db_mode",   2)
modparam("usrloc", "matching_mode", 1)
modparam("usrloc", "cseq_delay", 5)
modparam("usrloc", "accept_replicated_contacts", 0)
modparam("usrloc", "skip_replicated_db_ops", 1)

#### User Agent Client module
loadmodule "uac_auth.so"
loadmodule "uac.so"
loadmodule "uac_registrant.so"
modparam("uac","restore_mode","auto")
modparam("uac_registrant", "timer_interval", 120)
modparam("uac_registrant", "hash_size", 2)


#### REGISTRAR module
loadmodule "registrar.so"

modparam("registrar", "received_avp", "$avp(42)")


/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)

# ----- auth_db params -----
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "")

# ----- domain params -----

#modparam("domain", "db_url",DBURL)
modparam("domain", "db_mode", 1)   # Use caching

#modparam("alias_db", "db_url", DBURL)

# ----- multi-module params -----
/* uncomment the following line if you want to enable multi-domain support
   in the modules (dafault off) */

# ----- Dialog params ----
loadmodule "dialog.so"
modparam("dialog", "enable_stats", 0)


# ------ NAT Traversal Module ----

loadmodule "nat_traversal.so"
modparam("nat_traversal", "keepalive_interval", 60)
modparam("nat_traversal", "keepalive_method", "OPTIONS")
modparam("nat_traversal", "keepalive_from", "sip:pinger@XXX")
modparam("nat_traversal", "keepalive_extra_headers", "X-KeepAliveHeader: Is there any out there?\r\n")
modparam("nat_traversal", "keepalive_state_file", "/tmp/opensips_keepalive_state")

# ----- Load-Balancer params ---- Switched to stateless dispatcher
#loadmodule "load_balancer.so"
#modparam("load_balancer", "db_url", DBURL)
#modparam("load_balancer", "probing_interval", 60)
#modparam("load_balancer", "probing_reply_codes", "404, 200, 481")
#modparam("load_balancer", "probing_method", "INFO")


# ----- DISPATCHER Module ------
loadmodule "dispatcher.so"
modparam("dispatcher", "flags", 3)
modparam("dispatcher", "attrs_avp", "$avp(attrs_avp)")
modparam("dispatcher", "ds_ping_method", "INFO")
modparam("dispatcher", "ds_ping_from", "sip:dispatcher@XXX")
modparam("dispatcher", "ds_ping_interval", 30)
modparam("dispatcher", "ds_probing_threshhold", 10)
modparam("dispatcher", "options_reply_codes", "404, 200, 481")


# ------AVPops Module params ------
loadmodule "avpops.so"


# ------Stun Module -------

loadmodule "stun.so"
modparam("stun", "primary_ip", "XXX")
modparam("stun", "alternate_ip", "172.16.3.8")

# ------Presence User Agent Module -----

loadmodule "xcap.so"
loadmodule "xcap_client.so"

loadmodule "pua.so"
loadmodule "pua_mi.so"
loadmodule "pua_dialoginfo.so"
loadmodule "presence.so"
loadmodule "presence_dialoginfo.so"
loadmodule "presence_xml.so"
loadmodule "presence_mwi.so"
loadmodule "presence_xcapdiff.so"
loadmodule "rls.so"
loadmodule "mi_xmlrpc.so"

modparam("xcap", "integrated_xcap_server", 1)
modparam("xcap_client", "periodical_query", 0)

modparam("mi_xmlrpc", "port", 8888)

modparam("presence", "server_address", "sip:precense@XXX:5060")
modparam("presence", "fallback2db", 1)
modparam("presence", "clean_period",  30)

modparam("presence_xml", "force_active", 0)
modparam("presence_xml", "pidf_manipulation", 1)

modparam("rls", "server_address", "sip:rls@XXX:5060")
modparam("rls", "to_presence_code", 5)

modparam("pua_dialoginfo", "presence_server", "sip:precense@XXX:5060")


# ---- DBURL for all modules ------

modparam("avpops|alias_db|domain|auth_db|uac_registrant|usrloc|dialog|pua|presence|xcap|dispatcher", "db_url", "mysql://opensips:XXX@db1.local/opensips")

####### Routing Logic ########
# main request routing logic

route{

# ### Here will be list of proxies. Yes, it's duplicating with MySQL base, but I've not figured what to be best.
# may be here to make db queries.... Ok, let's make some tests later;

# Group 1
        $var(external_media_proxy) = "XXX";
# Group 2
        $var(internal_media_proxy) = "172.16.3.2";
# Group 3
        $var(media_server) = "172.16.3.16";
# Group 4
    $var(webrtc_proxy) = "172.16.3.7";

# ### Var ends


Небольшая защита от сканеров




        if (search("friendly-scanner|sipvicious|sipcli*")) {
        xlog("L_INFO", "IS_FRIENDLYSCANNER_MESSAGE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        exit;
    }

        if (search_body("friendly-scanner|sipvicious|sipcli")) {
        xlog("L_INFO", "IS_FRIENDLYSCANNER_BODY: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        exit;
    }


   

    if (!mf_process_maxfwd_header("10")) {
                xlog("L_INFO", "TOO_MANY_HOPS: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                exit;
        }


Этот кусок работает как-то странно и часто отвечает 503. Особенно в случае doubango webrtc2sip, хотя он уже не используется. На период тестов он закомменчен.


#        if (msg:len >= 4096 ) {
#                xlog("L_INFO", "MESSAGE_TOO_BIG: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
#                sl_send_reply("513", "Message too big");
#                exit;
#        }

        force_rport();

        if (client_nat_test("3")) {
                xlog("L_INFO", "CLIENT_NAT_TEST: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        fix_contact();
                if (is_method("REGISTER|SUBSCRIBE")) {
                        xlog("L_INFO", "CLIENT_NAT_TEST_REGISTERSUBSCRIBEINVITE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        nat_keepalive();
                }
        }   


    #Precense and BLF part....

    if (uri==myself) {
        if( is_method("PUBLISH|SUBSCRIBE|NOTIFY")) {
            route(PRESENCE);
        }
    }

        #Client behind NAT
   
        route(TO_TAG);

        route(CANCEL);

    route(IS_FROM_MEDIA_INTERNAL);

    route(IS_FROM_MEDIA_EXTERNAL);

    route(IS_FROM_MEDIASERVER);
   
    route(IS_FROM_WEB);

    route(IS_FROM_PROVIDER);

  
        # authenticate if from local subscriber. Works for local users, but not for REGISTER
        if (!(method =="REGISTER") && is_from_local()) {
                xlog("L_INFO", "NOTREGISTRATIONANDLOCAL: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                if (!proxy_authorize("", "subscriber")) {
                        proxy_challenge("", "0");
                        xlog("L_INFO", "NOTREGISTRATIONANDLOCAL_NOTPROXYAUTHORIZE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        exit;
                } else {
                        xlog("L_INFO", "NOTREGISTRATIONANDLOCAL_PROXYAUTHORIZE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                }
                if (!db_check_from()) {
                        send_reply("403","Forbidden auth ID");
                        xlog("L_INFO", "NOTREGISTRATIONANDLOCAL_NOTDBCHECKFROM: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        exit;
                } else {
                        xlog("L_INFO", "NOTREGISTRATIONANDLOCAL_DBCHECKFROM: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                }
                xlog("L_INFO", "NOTREGISTRATIONANDLOCAL_CONSUMECREDENTIALS: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                consume_credentials();
                # caller authenticated
        }

        # preloaded route checking
        route(LOOSE_ROUTE);

        # record routing
        if (!is_method("REGISTER|MESSAGE")) {
                xlog("L_INFO", "NOTREGISTERORMESSAGE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                record_route();
        }

        # account only INVITEs
        #if (is_method("INVITE")) {
        #        xlog("L_INFO", "INVITEACCOUNTING: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        #        setflag(1); # do accounting
        #}

        # if not a targetting a local SIP domain, just send it out
        # based on DNS (calls to foreign SIP domains)
        if (!is_uri_host_local()) {
                xlog("L_INFO", "NOTLOCALURI: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                append_hf("P-hint: outbound\r\n");
                t_on_failure("1");
                resetflag(8);
                route(1);
        }


        # requests for my domain
        # Register users on OpenSIPS

        if (is_method("REGISTER")) {
                xlog("L_INFO", "REGISTER: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                # authenticate the REGISTER requests
                if (!www_authorize("", "subscriber")) {
                        xlog("L_INFO", "REGISTER_NOTWWWAUTHORIZE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        www_challenge("", "0");
                        exit;
                } else {
                        xlog("L_INFO", "REGISTER_WWWAUTHORIZE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                }
                if (!db_check_to()) {
                        xlog("L_INFO", "REGISTER_NOTDBCHECKTO: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        send_reply("403","Forbidden auth ID");
                        exit;
                } else {
                        xlog("L_INFO", "REGISTER_DBCHECKTO: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                }

                if (!save("location")) {
                        xlog("L_INFO", "REGISTER_NOTSAVELOCATION: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        sl_reply_error();
                } else {
                        xlog("L_INFO", "REGISTER_SAVELOCATION: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                }
                exit;
        }

        # Some error processing
        route(ERROR);
        #Change aliases
        if(alias_db_lookup("dbaliases")) {
                xlog("L_INFO", "ALIASDBLOOKUP: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        }


        # Nave we found our user?
        if (!lookup("location")) {
                xlog("L_INFO", "NOTLOOKUPLOCATION: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                # Let's try here to loadbalance it to mediaproxy :)
                if (is_method("INVITE")) {
                        xlog("L_INFO", "LOOKUPLOCATION_INVITE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        ds_select_dst("1", "4");
                } else {
                        xlog("L_INFO", "LOOKUPLOCATION_NOTINVITE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        if (is_method("OPTIONS")) {
                                #Answer OPTIONS anyway
                                xlog("L_INFO", "LOOKUPLOCATION_NOTINVITE_OPTIONS: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                                sl_send_reply("200", "ALIVE OK");
                        }
                        exit;
                }
        } else {
                xlog("L_INFO", "LOOKUPLOCATION: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        }

        # when routing via usrloc, log the missed calls also
        #setflag(2);

        # arm a failure route in order to catch failed calls
        # targeting local subscribers; if we fail to deliver
        # the call to the user, we send the call to voicemail
        t_on_failure("1");
        resetflag(8);

        route(1);
}

route[1] {
        # Just relay it forward....
        if (!t_relay()) {
                xlog("L_INFO", "NOTTRELAY: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                sl_reply_error();
        }
    if (is_method("OPTIONS")) {
        xlog("L_INFO", "OPTIONS_ANSWER: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        sl_send_reply("200", "ALIVE OK");
    }
        exit;
}


route[TO_TAG] {
        if (has_totag()) {
                xlog("L_INFO", "HAS_TO_TAG: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                # sequential request withing a dialog should
                # take the path determined by record-routing
                if (loose_route()) {
                        route(1);
                } else {
                        if ( is_method("ACK") ) {
                                xlog("L_INFO", "HAS_TO_TAG_NOTLR_ACK: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                                if ( t_check_trans() ) {
                                        # non loose-route, but stateful ACK; must be an ACK after
                                        # a 487 or e.g. 404 from upstream server
                                        xlog("L_INFO", "HAS_TO_TAG_NOTLR_ACK_TRANS: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                                        t_relay();
                                        exit;
                                } else {
                                        # ACK without matching transaction ->
                                        # ignore and discard
                                        xlog("L_INFO", "HAS_TO_TAG_NOTLR_ACK_NOTTRANS: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                                        exit;
                                }
                        }
                        xlog("L_INFO", "HAS_TO_TAG_NOTLR: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        send_reply("404","Not here");
                }
                exit;
        }
}


route[CANCEL] {
        # CANCEL processing
        if (is_method("CANCEL")) {
                xlog("L_INFO", "CANCEL: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                if (t_check_trans())
                        t_relay();
                exit;
        }

        if (t_check_trans()) {
                xlog("L_INFO", "TRANSACTION: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        }

}

route[LOOSE_ROUTE] {
        if (loose_route()) {
                xlog("L_INFO", "LOOSEROUTE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                if (!is_method("ACK"))
                        xlog("L_INFO", "LOOSEROUTE_ACK: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        send_reply("403","Preload Route denied");
                exit;
        }
}

route[ERROR] {
        if ($rU==NULL) {
                # request with no Username in RURI
                xlog("L_INFO", "RURIISNULL: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                if (is_method("OPTIONS")) {
                        xlog("L_INFO", "RURIISNULL_OPTIONS: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        send_reply("200","ALIVE OK");
                }
                else {
                        send_reply("484","Address Incomplete");
                }
                exit;
        }
}


failure_route[1] {
        # authentication reply received?
        if ( t_check_status("40[17]") ) {
                # have we already tried to authenticate?
                if (isflagset(8)) {
                        t_reply("503","Authentication failed");
                        exit;
                }

Кусок, который отвечает за регистрацию на внешних сервисах. Т.е. ту последовательность REGISTER - 401 - REGISTER (digest) - 200
                if (uac_auth()) {
                        # mark that auth was performed
                        setflag(8);
                        # trigger again the failure route
                        t_on_failure("1");
                        # repeat the request with auth response this time
                        append_branch();
                        t_relay();
                }
        }

        if (t_was_cancelled()) {
                xlog("L_INFO", "FAILUREROUTE_TWASCANCELLED: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                exit;
        }

        # if the failure code is "408 - timeout" or "486 - busy",
        if (t_check_status("486|408")) {
                xlog("L_INFO", "FAILUREROUTE_TCHECKSTATUS: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                exit;
        }
}


route[ROUTINE] {

        route(CANCEL);
        route(LOOSE_ROUTE);
        route(ERROR);
}


route[IS_FROM_MEDIA_INTERNAL] {
        if ($(var(internal_media_proxy){param.exist,$si})) {
                xlog("L_INFO", "IS_FROM_MEDIA_INTERNAL: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                if (is_method("REGISTER|MESSAGE")) {
                        sl_send_reply("500", "Method not allowed");
                }
                route(ROUTINE);

                if (is_method("INVITE")) {
                        ds_select_dst("3", "4");
                }
                route(1);
                exit;
        }
    xlog("L_INFO", "IS_NOTFROM_MEDIA_INTERNAL: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
}

route[IS_FROM_MEDIA_EXTERNAL] {
        if ($(var(external_media_proxy){param.exist,$si})) {
                xlog("L_INFO", "IS_FROM_MEDIA_EXTERNAL: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci] + DD = $dd / $ds");
                if (is_method("REGISTER|MESSAGE")) {
                        sl_send_reply("500", "Method not allowed");
            exit;
                }
                route(ROUTINE);

        if (is_method("INVITE")) {
            if (!registered("location") && is_uri_host_local()) {
                xlog("L_INFO", "IS_FROM_MEDIA_EXTERNAL_INVITE_USER_NOTREG: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                sl_send_reply("404", "User not found");
                exit;
            } else {
                xlog("L_INFO", "IS_FROM_MEDIA_EXTERNAL_INVITE_USERREG: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
            }
        }
   

                route(1);

                exit;
        }
    xlog("L_INFO", "IS_NOTFROM_MEDIA_EXTERNAL: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
}

route[IS_FROM_MEDIASERVER] {
        if ($(var(media_server){param.exist,$si})) {
                xlog("L_INFO", "IS_FROM_MEDIASERVER: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                if (is_method("REGISTER|MESSAGE")) {
                        sl_send_reply("500", "Method not allowed");
            exit;
                }

                route(ROUTINE);

Тут разделение. В качестве фронта для webrtc выступает FreeSwitch, и на нем регаются клиенты с префиксом web_

                if (is_method("INVITE")) {
            if (pcre_match("$oU","^web_")) {
                            ds_select_dst("4", "4");
            } else {
                            ds_select_dst("2", "4");
            }
                }
                route(1);

                exit;
        }
    xlog("L_INFO", "IS_NOTFROM_MEDIASERVER: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
}


Для провайдеров заведена отдельная табличка.

route[IS_FROM_PROVIDER] {
    avp_db_query("SELECT IF( EXISTS( SELECT name FROM providers WHERE ip_address = INET_ATON('$si')), 1, 0)","$avp(is_provider)");
    if ($avp(is_provider)) {
        xlog("L_INFO", "IS_FROM_PROVIDER: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
        # Here will be some ideas about providers not handling DID. I really don't know what to do so. Test will tell us
        if (is_method("REGISTER|MESSAGE")) {
            xlog("L_INFO", "IS_FROM_PROVIDER_REGISTERORMESSAGE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
            sl_send_reply("500", "Method not allowed");
        }
        route(ROUTINE);
        if (is_method("INVITE")) {
            xlog("L_INFO", "IS_FROM_PROVIDER_INVITE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
            ds_select_dst("1", "4");
        }
        route(1);
        exit;
    }
    xlog("L_INFO", "IS_NOTFROM_PROVIDER: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
}


route[IS_FROM_WEB] {
        if ($(var(webrtc_proxy){param.exist,$si})) {
                xlog("L_INFO", "IS_FROM_WEB: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                if (is_method("REGISTER|MESSAGE")) {
            xlog("L_INFO", "IS_FROM_WEB_REGISTERORMESSAGE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        sl_send_reply("500", "Method not allowed");
            exit;
                }

                route(ROUTINE);

                if (is_method("INVITE")) {
            xlog("L_INFO", "IS_FROM_WEB_INVITE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                        ds_select_dst("3", "4");
                }
                route(1);

                exit;
        }
    xlog("L_INFO", "IS_NOTFROM_WEB: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");

}

route[PRESENCE] {
        if (!t_newtran()) {
            xlog("L_INFO", "PRESENCE_NOTNEWTRAN: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
            sl_reply_error();
            exit;
        };

        if(is_method("PUBLISH")) {
            xlog("L_INFO", "PRESENCE_PUBLISH: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
            handle_publish();
        } else {
            if(is_method("SUBSCRIBE")) {
                xlog("L_INFO", "PRESENCE_SUBSCRIBE: [F=$fu R=$ru D=$du M=$rm IP=($si:$sp $Ri:$Rp) ID=$ci]");
                rls_handle_subscribe();
                handle_subscribe();
            }
        }
        exit;
}



Да, тут много лишнего и некрасивого, но работа идет. В перспективе - интеграция с OpenXCAP для пристутствия и поддержка SIP_MESSAGE.
Но топология скрывается и звук ходит, что меня не может не радовать.

https://code.google.com/p/opensips-freeswitch-sbc/source/browse/ - Тут мои конфиги для opensips и freeswitch

-- upd.
Залита новая версия конфига. Добавлена авторизация провайдера по IP, добавлена некая защита от сканеров. Пока нету теста при регистрациях на внешних серверах. Также добавлена поддержка BLF (не проверено)
-- upd 2
Залит новый конфиг. Миграция на 1.11,  load_balancer сменен на dispatcher (потому что stateless), nathelper -> nat_traversal (потому что rtpproxy не используется), выпилен модуль accounting. Ну и мелкие изменения по ходу дела.
-- upd 3
На Гуглокод залит только конфиг OpenSIPS 1.11, теперь он может работать как  proxy-auth, т.е. полностью на себя берет вопрос аутентификации при звонке на внешние сервисы. Для этого пришлось сделать еще одну табличку в mysql с параметрами username, password, realm.