Вот примерно такой сервис сделали....
Сейчас звонит только на мою голосовую почту, не так много у меня денег, чтобы напрямую на мобильный мне звонило, а 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.
Thursday, February 21, 2013
LiveStreaming для тех, кого там нет
Возникла немного нетипичная задачка - устроить людям вещание в интернете. Для этого можно использовать много средств, но мы будем использовать Flussonic - в бесплатной версии он умеет все, что нам надо. Запись - это дело десятое.
После долгих мучений и репозиториев был выбран дистрибутив Ubuntu Server 12.04.2 LTS.
Устанавливается крайне просто:
echo "deb http://binaries.erlang-solutions.com/debian precise contrib" >> /etc/apt/sources.list
echo "deb http://debian.erlyvideo.org binary/" >> /etc/apt/sources.list
wget -O - http://binaries.erlang-solutions.com/debian/erlang_solutions.asc | sudo apt-key add -
wget -q -O - http://debian.erlyvideo.org/binary/gpg.key | apt-key add -
apt-get update
apt-get -y install esl-erlang flussonic
Итак, источников вещания у нас 2.
1. IP-камера Axis 210 (Такое себе старье без H.264).
Будем вещать через RTSP. Ее надо перекодировать. Для этого будем использовать VLC
apt-get install vlc x264
VLC выкладывает http поток на свой порт 9090, оттуда его забирает Flussonic и отдает JwPlayer'у RTSP поток.
файл startvlc.sh
#!/bin/sh
TR='transcode{venc=x264{cabac=yes,bframes=0,keyint=125,qpmin=20,qpmax=50,ref=5,merange=24,mixed-refs=yes,direct=auto,me=umh,subme=7,trellis=2,weightb=yes,partitions=all,vbv-maxrate=920,vbv-bufsize=9000,ratetol=100.0,scenecut=60},vb=820,,width=720,height=576,hurry-up,audio-sync,deinterlace=yes,acodec=aac,aenc=avcodec{strict=-2},ab=64,channels=2,samplerate=48000}'
OUT="std{access=http,mux=ts{use-key-frames=1},dst=:9090}"
cvlc -vvvv rtsp://root:passw@CAMERA-IP/mpeg4/media.amp --intf dummy --sout '#'${TR}:${OUT}
echo "{stream, "vlc.ts", "tshttp://VLC-IP:9090/"}." >> /etc/flussonic/flussonic.conf
/etc/init.d/flussonic start
После этого в панели по адресу FLUSSONIC-IP:8080 убедимся, что поток виден и его можно посмотреть.
Примерный код для index.html
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js">
</script>
<script type="text/javascript">
var flashvars = {
file : "vlc.ts",
streamer:'rtmp://FLUSSONIC-IP:1935',
'rtmp.tunneling':false,
autostart: true
};
var paramObj = {allowScriptAccess : "always", allowFullScreen : "true", allowNetworking : "all"};
swfobject.embedSWF("http://FLUSSONIC-IP:8080/flu/jwplayer.swf", "video", 720, 576,
"10.3", "/flu/expressInstall.swf",
flashvars, paramObj, {name: "jwplayer"});
</script>
2. Adobe FLME.
Будем забирать по HDS (что бы это ни значило :) )
Тут немного проще, указываем FMLE в адресе сервера наш flussonic в виде
Адрес: rtmp://FLUSSONIC-IP/live
Поток: livestream
AFLME выкладывает RTMP поток на Flussonic, оттуда он рестримит по HDS плееру.
В панели по адресу FLUSSONIC-IP:8080 убедимся, что поток виден и его можно посмотреть.
Примерный код для index.html
<div id="video"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js">
</script>
<script type="text/javascript">
var flashvars = {
src : "http://FLUSSONIC-IP:8080/live/livestream/manifest.f4m",
autoPlay: true
};
var paramObj = {allowScriptAccess : "always", allowFullScreen : "true", allowNetworking : "all"};
swfobject.embedSWF("http://FLUSSONIC-IP:8080/flu/StrobeMediaPlayback.swf", "video", 720, 576,
"10.3", "/flu/expressInstall.swf",
flashvars, paramObj, {name: "StrobeMediaPlayback"});
</script>
Вообще, советую приобщиться к инструкциям на сервере проекта :) Для знающих людей они будут полезнее моих :)
После долгих мучений и репозиториев был выбран дистрибутив Ubuntu Server 12.04.2 LTS.
Устанавливается крайне просто:
echo "deb http://binaries.erlang-solutions.com/debian precise contrib" >> /etc/apt/sources.list
echo "deb http://debian.erlyvideo.org binary/" >> /etc/apt/sources.list
wget -O - http://binaries.erlang-solutions.com/debian/erlang_solutions.asc | sudo apt-key add -
wget -q -O - http://debian.erlyvideo.org/binary/gpg.key | apt-key add -
apt-get update
apt-get -y install esl-erlang flussonic
Итак, источников вещания у нас 2.
1. IP-камера Axis 210 (Такое себе старье без H.264).
Будем вещать через RTSP. Ее надо перекодировать. Для этого будем использовать VLC
apt-get install vlc x264
VLC выкладывает http поток на свой порт 9090, оттуда его забирает Flussonic и отдает JwPlayer'у RTSP поток.
файл startvlc.sh
#!/bin/sh
TR='transcode{venc=x264{cabac=yes,bframes=0,keyint=125,qpmin=20,qpmax=50,ref=5,merange=24,mixed-refs=yes,direct=auto,me=umh,subme=7,trellis=2,weightb=yes,partitions=all,vbv-maxrate=920,vbv-bufsize=9000,ratetol=100.0,scenecut=60},vb=820,,width=720,height=576,hurry-up,audio-sync,deinterlace=yes,acodec=aac,aenc=avcodec{strict=-2},ab=64,channels=2,samplerate=48000}'
OUT="std{access=http,mux=ts{use-key-frames=1},dst=:9090}"
cvlc -vvvv rtsp://root:passw@CAMERA-IP/mpeg4/media.amp --intf dummy --sout '#'${TR}:${OUT}
echo "{stream, "vlc.ts", "tshttp://VLC-IP:9090/"}." >> /etc/flussonic/flussonic.conf
/etc/init.d/flussonic start
После этого в панели по адресу FLUSSONIC-IP:8080 убедимся, что поток виден и его можно посмотреть.
Примерный код для index.html
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js">
</script>
<script type="text/javascript">
var flashvars = {
file : "vlc.ts",
streamer:'rtmp://FLUSSONIC-IP:1935',
'rtmp.tunneling':false,
autostart: true
};
var paramObj = {allowScriptAccess : "always", allowFullScreen : "true", allowNetworking : "all"};
swfobject.embedSWF("http://FLUSSONIC-IP:8080/flu/jwplayer.swf", "video", 720, 576,
"10.3", "/flu/expressInstall.swf",
flashvars, paramObj, {name: "jwplayer"});
</script>
2. Adobe FLME.
Будем забирать по HDS (что бы это ни значило :) )
Тут немного проще, указываем FMLE в адресе сервера наш flussonic в виде
Адрес: rtmp://FLUSSONIC-IP/live
Поток: livestream
AFLME выкладывает RTMP поток на Flussonic, оттуда он рестримит по HDS плееру.
В панели по адресу FLUSSONIC-IP:8080 убедимся, что поток виден и его можно посмотреть.
Примерный код для index.html
<div id="video"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js">
</script>
<script type="text/javascript">
var flashvars = {
src : "http://FLUSSONIC-IP:8080/live/livestream/manifest.f4m",
autoPlay: true
};
var paramObj = {allowScriptAccess : "always", allowFullScreen : "true", allowNetworking : "all"};
swfobject.embedSWF("http://FLUSSONIC-IP:8080/flu/StrobeMediaPlayback.swf", "video", 720, 576,
"10.3", "/flu/expressInstall.swf",
flashvars, paramObj, {name: "StrobeMediaPlayback"});
</script>
Вообще, советую приобщиться к инструкциям на сервере проекта :) Для знающих людей они будут полезнее моих :)
Wednesday, February 20, 2013
Экзотика с кодеками или silk+amrnb в Asterisk
Возникла задачка гонять голос по 3G сетям. После долгих и не особенно продуктивных мучений были выбраны кодеки speex и g729, как кандидаты на релиз. amr-nb патч существовал для версии 1.8, у меня же по разным причинам используется 10ка.
Но выбравшись из консоли и взглянув на мир, выяснилось, что для 10ки можно использовать кодек silk, он же используется для Skype и с недавнего времени стал open-source. Плюс, добрый человек в Одессе сделал патч для поддержки amrnb в 10й версии Астериска.
Все собиралось и тестировалось на Asterisk 10.12.1, Debian 6.0 x64 (OpenVZ)
1. SILK
Собирался по мотивам https://github.com/mordak/codec_silk
К стандартному набору для компилирования необходимо добавить curl. Это для работы скрипта, но никто не мешает скачать все руками.
Из отличий - в install_silk.sh заменил
ln -sf "SILK_SDK_SRC_FIX_v$SKYPE_SILK_VER" silk
на ln -sf "SILK_SDK_SRC_FLP_v$SKYPE_SILK_VER" silk Почему-то версия с фиксированной точкой у меня собираться отказывалась.
Также в astdir/codecs/silk/Makefile заменить
CFLAGS += -Wall -enable-threads -O3
на
CFLAGS += -Wall -enable-threads -O3 -fPIC
После этого все как обычно собирается.
После инсталляции в файл /etc/asterisk/codecs.conf добавить
[silk8]
type=silk
samprate=8000
fec=true
packetloss_percentage=10
maxbitrate=20000
dtx=false
[silk12]
type=silk
samprate=12000
fec=true
packetloss_percentage=10
maxbitrate=25000
dtx=false
[silk16]
type=silk
samprate=16000
fec=true
packetloss_percentage=10
maxbitrate=30000
dtx=false
[silk24]
type=silk
samprate=24000
fec=true
packetloss_percentage=10
maxbitrate=40000
dtx=false
Ну и в консоли.: asterisk -rx "core restart now"
И да, я знаю o бинарниках от Digium, но это не путь истинного джедая. Плюс, тот же добрый человек из Одессы упрекает их в глючности и не верить ему у меня оснований нет.
2. AMR-NB
Качаем патч с этой странички. Единственное что, если silk не компилируется вручную, то падает 2й hunk при патче. Просто в /codecs/Makefile необходимо добавить строчку
$(if $(filter codec_amr,$(EMBEDDED_MODS)),modules.link,codec_amr.so): $(LIBAMR)
После этого разархивируем в папку /codecs/amr содержимое следующего архива.
Переходим в папку /codecs/amr
./configure CFLAGS = "-g -O2 -fPIC"
make
После этого все как обычно собирается.
После инсталляции в файл /etc/asterisk/codecs.conf добавить
[amr]
octet-aligned=0
;codec_amr: Must be one of MR475, MR515, MR59, MR67, MR74, MR795, MR102, MR122
mode = MR475
dtx=0
cng = 1
vad = 0
octet-aligned=0 - Это следствие того, что CSipSimple (точнее, pjsip) умеет AMR только в Bandwidth-Efficient режиме. Что это такое, можно почитать в RFC.
Ну и в консоли.: asterisk -rx "core restart now"
Теперь по core show translation можно увидеть, что у нас появился целый набор silk'ов и amr.
Но выбравшись из консоли и взглянув на мир, выяснилось, что для 10ки можно использовать кодек silk, он же используется для Skype и с недавнего времени стал open-source. Плюс, добрый человек в Одессе сделал патч для поддержки amrnb в 10й версии Астериска.
Все собиралось и тестировалось на Asterisk 10.12.1, Debian 6.0 x64 (OpenVZ)
1. SILK
Собирался по мотивам https://github.com/mordak/codec_silk
К стандартному набору для компилирования необходимо добавить curl. Это для работы скрипта, но никто не мешает скачать все руками.
Из отличий - в install_silk.sh заменил
ln -sf "SILK_SDK_SRC_FIX_v$SKYPE_SILK_VER" silk
на ln -sf "SILK_SDK_SRC_FLP_v$SKYPE_SILK_VER" silk Почему-то версия с фиксированной точкой у меня собираться отказывалась.
Также в astdir/codecs/silk/Makefile заменить
CFLAGS += -Wall -enable-threads -O3
на
CFLAGS += -Wall -enable-threads -O3 -fPIC
После этого все как обычно собирается.
После инсталляции в файл /etc/asterisk/codecs.conf добавить
[silk8]
type=silk
samprate=8000
fec=true
packetloss_percentage=10
maxbitrate=20000
dtx=false
[silk12]
type=silk
samprate=12000
fec=true
packetloss_percentage=10
maxbitrate=25000
dtx=false
[silk16]
type=silk
samprate=16000
fec=true
packetloss_percentage=10
maxbitrate=30000
dtx=false
[silk24]
type=silk
samprate=24000
fec=true
packetloss_percentage=10
maxbitrate=40000
dtx=false
Ну и в консоли.: asterisk -rx "core restart now"
И да, я знаю o бинарниках от Digium, но это не путь истинного джедая. Плюс, тот же добрый человек из Одессы упрекает их в глючности и не верить ему у меня оснований нет.
2. AMR-NB
Качаем патч с этой странички. Единственное что, если silk не компилируется вручную, то падает 2й hunk при патче. Просто в /codecs/Makefile необходимо добавить строчку
$(if $(filter codec_amr,$(EMBEDDED_MODS)),modules.link,codec_amr.so): $(LIBAMR)
После этого разархивируем в папку /codecs/amr содержимое следующего архива.
Переходим в папку /codecs/amr
./configure CFLAGS = "-g -O2 -fPIC"
make
После этого все как обычно собирается.
После инсталляции в файл /etc/asterisk/codecs.conf добавить
[amr]
octet-aligned=0
;codec_amr: Must be one of MR475, MR515, MR59, MR67, MR74, MR795, MR102, MR122
mode = MR475
dtx=0
cng = 1
vad = 0
octet-aligned=0 - Это следствие того, что CSipSimple (точнее, pjsip) умеет AMR только в Bandwidth-Efficient режиме. Что это такое, можно почитать в RFC.
Ну и в консоли.: asterisk -rx "core restart now"
Теперь по core show translation можно увидеть, что у нас появился целый набор silk'ов и amr.
Friday, December 14, 2012
A2Billing. Хочу быть провайдером.
Итак... Захотелось мне стать провайдером, благо дешевую международку я нашел. Теперь с каждой станцией буду давать немного трафика покупателям! И мне несложно, и плюшка приятная. Используется версия 2.0.1
1. LAMP! (+php-gd)
2. Ставим Asterisk с поддержкой realtime (для этого поставить дополнительно mysclclient-dev)
3. Качаем A2Billing и внимательно читаем файл INSTALL.rst и выполняем инструкции (только думая, некоторые пути имеет смысл заменить). Также обратите внимание, в скрипте установки баз MySQL могут быть неправильно прописаны пути для Debian 6. Пароли тоже не стоит ставить дефолтные :)
4. vi /etc/asterisk/res_config_mysql.conf
[general]
dbhost = 127.0.0.1
dbname = mya2billing
dbuser = a2billinguser
dbpass =
dbport = 3306
5. vi /etc/asterisk/extconfig.conf
[settings]
iaxusers => mysql,general,cc_iax_buddies
iaxpeers => mysql,general,cc_iax_buddies
sipusers => mysql,general,cc_sip_buddies
sippeers => mysql,general,cc_sip_buddies
6. Настраиваем провайдеров в /etc/asterisk/sip.conf
[MainProvider](!)
context=NoContext ; DID пока не настраивал
disallow=all
allow=alaw
dtmf=rfc2833
type=peer
canreinvite=no
[Trunk1](MainProvider)
host=XXXXX
[Trunk2](MainProvider)
host=XXXXXX
[Trunk3](MainProvider)
host=XXXXXX
7. Настраиваем A2Billing (По мотивам http://sysadminman.net/documentation.html)
7.1 Настраиваем Провайдера (PROVIDERS)
7.2 Настраиваем Транки
7.3 Создаем Rate Card (RATES)
7.4 Создаем/Импортируем Rates, привязывая его к Rate Card
7.5 Создаем Сall Plan, привязывая его к Rate Card (Делается при повторном выборе Call Plan) (Тут можно вырезать префикс международки)
7.5 Создаем покупателя, приписываем ему Call Plan (CUSTOMERS)
7.6 Создаем VoIP Profile для пользователя, можно автоматом, только необходимо потом проставить CallerID. Оттуда же делаем Asterisk reload
Работает так:
Покупатель аутентифицируется на нашей платформе. Потом просто звонит на необходимый ему номер. Профит!
Несколько параметров, на которые стоит обратить внимание:
global-base_currency (базовая валюта), после ее смены надо в разделе Billing-Currency List сделать update
agi-conf1-asterisk_version
global-asterisk_version
agi-conf1-answer_call
agi-conf1-play_audio
agi-conf1-say_rateinitial
agi-conf1-say_timetocall
agi-conf1-use_dnid Если YES, то система не будет просить ввести номер, на который звонить, будет использовать присланный.
1. LAMP! (+php-gd)
2. Ставим Asterisk с поддержкой realtime (для этого поставить дополнительно mysclclient-dev)
3. Качаем A2Billing и внимательно читаем файл INSTALL.rst и выполняем инструкции (только думая, некоторые пути имеет смысл заменить). Также обратите внимание, в скрипте установки баз MySQL могут быть неправильно прописаны пути для Debian 6. Пароли тоже не стоит ставить дефолтные :)
4. vi /etc/asterisk/res_config_mysql.conf
[general]
dbhost = 127.0.0.1
dbname = mya2billing
dbuser = a2billinguser
dbpass =
dbport = 3306
5. vi /etc/asterisk/extconfig.conf
[settings]
iaxusers => mysql,general,cc_iax_buddies
iaxpeers => mysql,general,cc_iax_buddies
sipusers => mysql,general,cc_sip_buddies
sippeers => mysql,general,cc_sip_buddies
6. Настраиваем провайдеров в /etc/asterisk/sip.conf
[MainProvider](!)
context=NoContext ; DID пока не настраивал
disallow=all
allow=alaw
dtmf=rfc2833
type=peer
canreinvite=no
[Trunk1](MainProvider)
host=XXXXX
[Trunk2](MainProvider)
host=XXXXXX
[Trunk3](MainProvider)
host=XXXXXX
7. Настраиваем A2Billing (По мотивам http://sysadminman.net/documentation.html)
7.1 Настраиваем Провайдера (PROVIDERS)
7.2 Настраиваем Транки
7.3 Создаем Rate Card (RATES)
7.4 Создаем/Импортируем Rates, привязывая его к Rate Card
7.5 Создаем Сall Plan, привязывая его к Rate Card (Делается при повторном выборе Call Plan) (Тут можно вырезать префикс международки)
7.5 Создаем покупателя, приписываем ему Call Plan (CUSTOMERS)
7.6 Создаем VoIP Profile для пользователя, можно автоматом, только необходимо потом проставить CallerID. Оттуда же делаем Asterisk reload
Работает так:
Покупатель аутентифицируется на нашей платформе. Потом просто звонит на необходимый ему номер. Профит!
Несколько параметров, на которые стоит обратить внимание:
global-base_currency (базовая валюта), после ее смены надо в разделе Billing-Currency List сделать update
agi-conf1-asterisk_version
global-asterisk_version
agi-conf1-answer_call
agi-conf1-play_audio
agi-conf1-say_rateinitial
agi-conf1-say_timetocall
agi-conf1-use_dnid Если YES, то система не будет просить ввести номер, на который звонить, будет использовать присланный.
Monday, October 29, 2012
GnuGk, как замена VBP-E и частично VBP-ST
Итак, зачем покупать VBP, если есть замечательный продукт GnuGk, который умеет все, что серия E и также H.480, что умеет ST-серия.
Далее немного подчищенной копипасты с сорцфорджа.
Собирается с поддержкой H.480.18/.19
Запускается все командой вида
/usr/src/openh323gk/obj_linux_x86_64_s/gnugk -c gatekeeper.ini -o /tmp/gnugk.log -tt &
Telnet на 7000 порту обеспечит просмотр статуса.
Небольшое дополнение:
В целях облегчения жизни себе и прочим поднял данный вариант на 92.249.69.203. Так что можете смело регистрироваться. Абсолютно бесплатно :)
Далее немного подчищенной копипасты с сорцфорджа.
Собирается с поддержкой H.480.18/.19
======================================================= Final Method - Installing GNUGK v3.1+ on Debian Squeeze ======================================================= cd /usr/src apt-get update apt-get install flex bison build-essential pkg-config automake libmysqlclient-dev libmysql++-dev
libsnmp-base libsnmp-dev libssl-dev wget http://backports.debian.org/debian-backports/pool/main/libs/libssh/libssh-4_0.5.2-1~bpo60+1_i386.deb dpkg -i libssh-4_0.5.2-1~bpo60+1_i386.deb wget http://backports.debian.org/debian-backports/pool/main/libs/libssh/libssh-dev_0.5.2-1~bpo60+1_i386.deb dpkg -i libssh-dev_0.5.2-1~bpo60+1_i386.deb
(Тут естественно, выбрать свою архитектуру)
# Get static version download - do not use SVN: wget http://sourceforge.net/projects/opalvoip/files/v3.10%20Luyten/Stable%207/ptlib-2.10.7.tar.bz2 tar -jxf ptlib-2.10.7.tar.bz2 mv ptlib-2.10.7 ptlib export PTLIBDIR=/usr/src/ptlib cd /usr/src/ptlib cat version.h ======= #define MAJOR_VERSION 2 #define MINOR_VERSION 10 #define BUILD_TYPE ReleaseCode #define BUILD_NUMBER 7 ======= ./configure make optnoshared cd /usr/src # Get from CVS Web Snapshot download: wget http://h323plus.cvs.sourceforge.net/viewvc/h323plus/h323plus/?view=tar -O h323plus.tar.gz export OPENH323DIR=/usr/src/h323plus cd h323plus cat version.h ==== #define MAJOR_VERSION 1 #define MINOR_VERSION 24 #define BUILD_TYPE ReleaseCode #define BUILD_NUMBER 1 ==== ./configure make optnoshared cd /usr/src # Get from CVS Web Snapshot download: wget http://openh323gk.cvs.sourceforge.net/viewvc/openh323gk/openh323gk/?view=tar -O gnugk.tar.gz tar -xzf gnugk.tar.gz cd openh323gk cat version.h === # define GNUGK_MAJOR_VERSION 3 # define GNUGK_MINOR_VERSION 2 # define GNUGK_BUILD_TYPE ReleaseCode # define GNUGK_BUILD_NUMBER 0 === ./configure --enable-h46018 --with-large-fdset=8192 make optnoshared
Далее пример моего конфига gatekeeper.ini
[Gatekeeper::Main] Fortytwo=42 Name=GnuGk TimeToLive=600 StatusPort=7000 StatusTraceLevel=2 UseBroadcastListener=0
[RoutedMode] GKRouted=1 H245Routed=1 RemoveH245AddressOnTunneling=0 AcceptNeighborsCalls=1 AcceptUnregisteredCalls=1 SupportNATedEndpoints=1 DropCallsByReleaseComplete=1 CallSignalPort=1720
EnableH46018=1 EnableH46017=1 Enable46018=1
[Proxy] Enable=1 InternalNetwork=10.10.0.0/255.255.0.0,127.0.0.0/8 T120PortRange=40000-40999 RTPPortRange=50000-59999 ProxyForNAT=1
[RasSrv::RRQFeatures]
AliasTypeFilter=terminal;dialeddigits AliasTypeFilter=gateway;h323id ; Это для того, чтобы могли регистрироваться Поликомы
[RasSrv::ARQFeatures] ArjReasonRouteCallToGatekeeper=1 ;; RemoveTrailingChar=# RoundRobinGateways=1
[RoutingPolicy] default=explicit,internal,catchall [Routing::CatchAll]
CatchAllAlias=inbound ; Сюда будут попадать все звонки напрямую на IP гейткипера.
;Другой вариант прописывания дефолтного маршрута:
;[RoutingPolicy] ;default=explicit,internal ;[Routing::Explicit] ;внутренний IP адрес=адрес перенаправления ;внешний IP адрес = адрес перенаправления
;тут вообще можно менять звонимые IP на все, что угодно
[Gatekeeper::Auth]
default=allow
[RasSrv::RRQAuth]
default=confirm
[GkStatus::Auth] rule=password
username=some_hash; Тут используется утилита addpasswd. В примере она не собирается,
но можно использовать версию из репозитория
Запускается все командой вида
/usr/src/openh323gk/obj_linux_x86_64_s/gnugk -c gatekeeper.ini -o /tmp/gnugk.log -tt &
Telnet на 7000 порту обеспечит просмотр статуса.
Небольшое дополнение:
В целях облегчения жизни себе и прочим поднял данный вариант на 92.249.69.203. Так что можете смело регистрироваться. Абсолютно бесплатно :)
Subscribe to:
Posts (Atom)