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.

7 comments:

  1. Игорь, добрый день!

    Хотел скачать конфиги opensips и freeswitch. Но по пройденной ссылке оказалось к сожалению пусто (((

    Буду очень благодарен если дадите посмотреть конфиги.

    Также интересно посмотреть настройки Астера.

    В заранее огромное спасибо!

    ReplyDelete
    Replies
    1. Подправил ссылку.
      По настройкам астера все просто. Дабы не заморачиваться, все валится в default контекст, там дальше запускается уже логика AGI, но это уже другая история :)

      Delete
  2. Здравствуйте, Игорь!

    Пытаюсь поставить opensips 1.11, точнее поставить не проблема, а вот с настройками...
    Получаю вот это и 403 ошибку в клиенте
    Attempt to route with preloaded Route's [sip:Konstantin@123.123.123.123/sip:Konstantin@123.123.123.123/sip:123.123.123.123/qlV7rJ7DeojOsCYUaHSSB2CBSf9x7w9W]

    Вроде все по родному мануалу, вот и Вашу статью просмотрел... У меня задача гораздо проще -- просто авторизация исвязь клиентов. Даже Астериск не нужен.
    Подскажите пож, куда копать?

    PS Нюанс лишь в том, что сам сервер находится за натом и имеет локальный IP, при обращении из вне, есть конечно внешний, с открытыми портами 5060, 5061...

    ReplyDelete
    Replies
    1. Это сложнее, тут надо отлаживать и смотреть в SIP пакеты перед тем, как их обрабатывать. У меня частично все завязано на адреса интерфейсов. Но вообще не зря проставлены по ходу дела xlog. Надо смотреть в файлик и смотреть где какие условия выполняются.

      Delete
  3. listen=udp:opensips_external_ip:5060 Тут прописать внешний адрес opensips.
    Вот например тут, я не могу указать реальный IP

    ReplyDelete
    Replies
    1. Тут указывается адрес интерфейса. Не обязательно внешний.

      Delete
  4. Nice post. Looking to integrate Opensips v3(with control panel) and Freeswitch or Asterisk based on this post in a Debian 9 or 10 machine. All my attempts failed. No user is able to register to Opensips currently. My opensips.cfg has a lot or errors. Is it possible to update this guide for newer versions, please?

    ReplyDelete