Вот примерно такой сервис сделали....
Сейчас звонит только на мою голосовую почту, не так много у меня денег, чтобы напрямую на мобильный мне звонило, а SIP я с собой не таскаю постоянно :)
Можете оставлять сообщения, они мне на почту будут приходить :)
позвонить
Friday, August 29, 2014
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/
Поэтому можно сделать некий примитивный 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.
Схема, к которой я пришел не отличается оптимизированностью, зато довольно проста в масштабировании (я надеюсь) и эксплуатации.
Схема:
WAN <-> OpenSIPS (1.8) -
| |
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.
Subscribe to:
Posts (Atom)