Documentation |
Documentation -> Tutorials -> Realtime OpenSIPS - Asterisk IntegrationThis page has been visited 32239 times. Table of Content (hide)
1. Realtime OpenSIPS - Asterisk IntegrationThis tutorial is made for OpenSIPS 1.8.x and Asterisk 1.8.x 1.1 ScopeThis tutorial presents the concept and implementation of a realtime integration of OpenSIPS SIP server and Asterisk media server. OpenSIPS is used a SIP server - users are registering with it, it routes calls, etc - while the purpose of Asterisk is to provide a full set of media services - like voicemail, conference, announcements, etc. It is a realtime integration because both OpenSIPS and Asterisk are provisioned in the same same time when comes to user accounts - when creating a new OpenSIPS users, automatically Asterisk will learn about it an provide and configure all necessary media services for it. Both OpenSIPS and Asterisk will be provisioned (for user accounts) via a shared mysql database. 1.2 Setup presentationThe following services will be offered by this integrated configuration:
1.3 Prerequisites1.4 OpenSIPSInstall OpenSIPS 1.8.x with mysql DB support (db_mysql module). if you use sources (tarballs or svn checkouts) do the following: $ make include_modules="db_mysql" prefix="/" all $ make include_modules="db_mysql" prefix="/" install 1.5 AsteriskInstall Asterisk 1.8 from local repository: # apt-get install asterisk Note: if your repository does not have the Asterisk 1.8 version, you must install a backported version of Asterisk, by following these steps: * add the following line in the /etc/apt/sources.lst file: deb http://backports.debian.org/debian-backports squeeze-backports main * update the repository: apt-get update * install asterisk from backports repository apt-get -t squeeze-backports install asterisk Install UnixODBC interface: # apt-get install unixodbc-dev libmyodbc # apt-get install asterisk-voicemail-odbcstorage The latest version of the Meetme application needs the DAHDI telephony interface. Install the Asterisk Dahdi application: # apt-get install asterisk-dahdi dahdi If a module package is not available for your platform, you must create your own package from dahdi sources, by issuing the following commands: # apt-get install dahdi-linux dahdi-source # cd /usr/src # m-a a-i dahdi # dpkg -i dahdi-modules-*.deb # modprobe dahdi 1.6 DB SetupOnly voicemail and conference services do require DB support. While the conference DB table will be exclusively be used by Asterisk (OpenSIPS does not require any information form there), the voicemail service do require a tight sharing of DB information about users between OpenSIPS and Asterisk. Considering OpenSIPS as the core SIP element of the platform, the core DB will be also belonging to OpenSIPS - we will use the OpenSIPS DB to drive both OpenSIPS and Asterisk. The tables required by Asterisk (for voicemail service) will be mapped over the OpenSIPS tables. This approach assumes two steps:
Creating the OpenSIPS tablesIn file /etc/opensips/opensipsctlrc enable the mysql backend (see DBENGINE) and also configure the host where the mysql server is located, the name to be used for creating the opensips table, the read-only (ro) and read-write (rw) usernames and passwords to be created for accessing the opensips DB - see all the varaibles with DB prefix in the file. Proceed with creation of the OpenSIPS standard database: $ opensipsdbctl create If you use the default values to DB host and mysql access users, you can access the DB by: $ mysql -h'localhost' -u'opensips' -p'opensipsrw' opensips > Once the default tables are created, we need to alter the subscriber table in order to add some additional fields required by the Asterisk DB view. These changes are:
Login to the opensips database (use the above login command) and run: > alter table subscriber add column `vmail_password` varchar(8) NOT NULL default '1234'; > alter table subscriber add column `first_name` varchar(25) NOT NULL default ''; > alter table subscriber add column `last_name` varchar(45) NOT NULL default ''; > alter table subscriber add column `email_address` varchar(50) NOT NULL default ''; > alter table subscriber add column `datetime_created` datetime NOT NULL default '0000-00-00 00:00:00'; Creating the Asterisk tablesCreate a separate database to be used by Asterisk - login as root into mysql server and run: > create database asterisk; > GRANT ALL PRIVILEGES ON asterisk.* TO 'asterisk' IDENTIFIED BY 'asterisk_pwd'; Create the Asterisk tables which are exclusively used only by Asterisk (as mysql tables): # create table for the meetme service CREATE TABLE `meetme` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `confno` varchar(80) NOT NULL DEFAULT '0', `username` varchar(64) NOT NULL DEFAULT '', `domain` varchar(128) NOT NULL DEFAULT '', `pin` varchar(20) DEFAULT NULL, `adminpin` varchar(20) DEFAULT NULL, `members` int(11) DEFAULT NULL, PRIMARY KEY (`confno`), UNIQUE KEY `id` (`id`) ); # create table to store the voicemail massages CREATE TABLE `voicemessages` ( `id` int(11) NOT NULL auto_increment, `msgnum` int(11) NOT NULL default '0', `dir` varchar(80) default '', `context` varchar(80) default '', `macrocontext` varchar(80) default '', `callerid` varchar(40) default '', `origtime` varchar(40) default '', `duration` varchar(20) default '', `mailboxuser` varchar(80) default '', `mailboxcontext` varchar(80) default '', `recording` longblob, PRIMARY KEY (`id`), KEY `dir` (`dir`) ); Create the Asterisk tables (as mysql views) that import the information from OpenSIPS tables: # create the asterisk users tables as a view over the OpenSIPS subscriber table CREATE VIEW `sipusers` AS select `opensips`.`subscriber`.`username` AS `name` ,_latin1'friend' AS `type`, NULL AS `secret`, `opensips`.`subscriber`.`domain` AS `host`, concat(`opensips`.`subscriber`.`rpid`,_latin1' ',_latin1'<',`opensips`.`subscriber`.`username`,_latin1'>') AS `callerid`, _latin1'default' AS `context`, `opensips`.`subscriber`.`username` AS `mailbox`, _latin1'yes' AS `nat`, _latin1'no' AS `qualify`, `opensips`.`subscriber`.`username` AS `fromuser`, NULL AS `authuser`, `opensips`.`subscriber`.`domain` AS `fromdomain`, NULL AS `insecure`, _latin1'no' AS `canreinvite`, NULL AS `disallow`, NULL AS `allow`, NULL AS `restrictcid`, `opensips`.`subscriber`.`domain` AS `defaultip`, `opensips`.`subscriber`.`domain` AS `ipaddr`, _latin1'5060' AS `port`, NULL AS `regseconds`, `opensips`.`subscriber`.`username` AS `defaultuser`, NULL AS `fullcontact`, `opensips`.`subscriber`.`domain` AS `regserver`, NULL AS `useragent`, 0 AS `lastms` from `opensips`.`subscriber`; # create the asterisk voceimail users table as a view over the OpenSIPS subscriber table CREATE VIEW `vmusers` AS select concat(`opensips`.`subscriber`.`username`,`opensips`.`subscriber`.`domain`) AS `uniqueid`, `opensips`.`subscriber`.`username` AS `customer_id`, _latin1'default' AS `context`, `opensips`.`subscriber`.`username` AS `mailbox`, `opensips`.`subscriber`.`vmail_password` AS `password`, _latin1'joe' AS `fullname`, `opensips`.`subscriber`.`email_address` AS `email`, NULL AS `pager`, `opensips`.`subscriber`.`datetime_created` AS `stamp` from `opensips`.`subscriber`; #create the asterisk voicemail aliases table as a view over the OpenSIPS dbaliases table CREATE VIEW `asterisk`.`vmaliases` AS select `opensips`.`dbaliases`.`alias_username` AS `alias`, _latin1'default' AS `context`, `opensips`.`dbaliases`.`username` AS `mailbox` from `opensips`.`dbaliases`; Configure the access for Asterisk to the created database (via unixodbc):
[MySQL] Description = MySQL driver Driver = /usr/lib/odbc/libmyodbc.so Setup = /usr/lib/odbc/libodbcmyS.so CPTimeout = CPReuse = UsageCount = 1
[MySQL-asterisk] Description = MySQL Asterisk database Trace = Off TraceFile = stderr Driver = MySQL SERVER = localhost USER = asterisk PASSWORD = asterisk_pwd PORT = 3306 DATABASE = asterisk
[asterisk] enabled => yes dsn => MySQL-asterisk username => asterisk password => asterisk_pwd pre-connect => yes
sipusers => odbc,asterisk,sipusers sippeers => odbc,asterisk,sipusers voicemail => odbc,asterisk,vmusers meetme => odbc,asterisk,meetme 1.7 Asterisk dialplanThe following extensions are set for Asterisk:
Set in /etc/asterisk/extensions.conf' : [general] static=yes writeprotect=no [default] ; Voicemail exten => _VMR_.,n,Ringing exten => _VMR_.,n,Wait(1) exten => _VMR_.,n,Answer exten => _VMR_.,n,Wait(1) exten => _VMR_.,n,Voicemail(${EXTEN:4}|u) exten => _VMR_.,n,Hangup ; Allow users to call their Voicemail directly exten => VM_pickup,n,Ringing exten => VM_pickup,n,wait(1) exten => VM_pickup,n,VoicemailMain(${CALLERIDNUM}|s) exten => VM_pickup,n,Hangup ; announcement: not available exten => AN_notavailable,1,Ringing exten => AN_notavailable,2,Playback(notavailable) exten => AN_notavailable,3,Hangup ; announcement: time exten => AN_time,1,Ringing exten => AN_time,2,Wait(1) exten => AN_time,3,SayUnixTime(,Europe/Bucharest,HMp) exten => AN_time,4,Hangup ; announcement:date exten => AN_date,1,Ringing exten => AN_date,2,SayUnixTime(,Europe/Bucharest,ABdY) exten => AN_date,3,Hangup ; announcement: echo exten => AN_echo,1,Ringing exten => AN_echo,2,Answer exten => AN_echo,3,Echo ; Conference service exten => _CR_.,1,Ringing exten => _CR_.,n,Wait(1) exten => _CR_.,n,MeetMe(${EXTEN:3}|Mi) 1.8 OpenSIPS configurationIn this example we take the OpenSIPS default config file that provides user registration and authentication against the DB and extends the script for adding the following media oriented services:
In /etc/opensips/opensips.cfg place (see the ASTERISK_HOOKS markers to find the script parts relevant to Asterisk integration): ####### Global Parameters ######### debug=3 log_stderror=no log_facility=LOG_LOCAL0 fork=yes children=4 /* uncomment the following lines to enable debugging */ #debug=6 #fork=no #log_stderror=yes port=5060 /* uncomment and configure the following line if you want opensips to bind on a specific interface/port/proto (default bind on all available) */ #listen=udp:192.168.1.2:5060 ####### Modules Section ######## #set module path mpath="/lib/opensips/modules/" loadmodule "db_mysql.so" loadmodule "signaling.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "mi_fifo.so" loadmodule "uri_db.so" loadmodule "uri.so" loadmodule "xlog.so" loadmodule "acc.so" loadmodule "auth.so" loadmodule "auth_db.so" loadmodule "domain.so" # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") # ----- rr params ----- # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- usrloc params ----- modparam("usrloc", "db_mode", 2) modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # ----- uri_db params ----- modparam("uri_db", "use_uri_table", 0) modparam("uri_db", "db_url", "") # ----- acc params ----- /* what sepcial events should be accounted ? */ modparam("acc", "early_media", 1) modparam("acc", "report_cancels", 1) /* account triggers (flags) */ modparam("acc", "failed_transaction_flag", 3) modparam("acc", "log_flag", 1) modparam("acc", "log_missed_flag", 2) /* uncomment the following lines to enable DB accounting also */ modparam("acc", "db_flag", 1) modparam("acc", "db_missed_flag", 2) # ----- auth_db params ----- modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("auth_db", "load_credentials", "") # ----- domain params ----- modparam("domain", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") modparam("domain", "db_mode", 1) # Use caching # ----- multi-module params ----- /* uncomment the following line if you want to enable multi-domain support in the modules (dafault off) */ modparam("alias_db|auth_db|usrloc|uri_db", "use_domain", 1) ####### Routing Logic ######## # main request routing logic route{ if (!mf_process_maxfwd_header("10")) { send_reply("483","Too Many Hops"); exit; } if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { if (is_method("BYE")) { setflag(1); # do accounting ... setflag(3); # ... even if the transaction fails } else if (is_method("INVITE")) { # even if in most of the cases is useless, do RR for # re-INVITEs alos, as some buggy clients do change route set # during the dialog. record_route(); } # route it out to whatever destination was set by loose_route() # in $du (destination URI). route(1); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after # a 487 or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction -> # ignore and discard exit; } } send_reply("404","Not here"); } exit; } #initial requests # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); # authenticate if from local subscriber if (!(method=="REGISTER") && is_from_local()) { if (!proxy_authorize("", "subscriber")) { proxy_challenge("", "0"); exit; } if (!check_from()) { send_reply("403","Forbidden auth ID"); exit; } consume_credentials(); # caller authenticated } # preloaded route checking if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) send_reply("403","Preload Route denied"); exit; } # record routing if (!is_method("REGISTER|MESSAGE")) record_route(); # account only INVITEs if (is_method("INVITE")) { 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()) { append_hf("P-hint: outbound\r\n"); route(1); } # requests for my domain if (is_method("REGISTER")) { # authenticate the REGISTER requests if (!www_authorize("", "subscriber")) { www_challenge("", "0"); exit; } if (!check_to()) { send_reply("403","Forbidden auth ID"); exit; } if (!save("location")) sl_reply_error(); exit; } if ($rU==NULL) { # request with no Username in RURI send_reply("484","Address Incomplete"); exit; } # ASTERISK HOOK - BEGIN # media service number? (digits starting with *) if ($rU=~"^\*[1-9]+") { # we do provide access to media services only to our # subscribers, who were previously authenticated if (!is_from_local()) { send_reply("403","Forbidden access to media service"); exit; } #identify the services and translate to Asterisk extensions if ($rU=="*1111") { # access to own voicemail IVR $ru = "sip:VM_pickup@ASTERISK_IP:ASTERISK_PORT"; } else if ($rU=="*2111") { # access to the "say time" announcement $ru = "sip:AN_time@ASTERISK_IP:ASTERISK_PORT"; } else if ($rU=="*2112") { # access to the "say date" announcement $ru = "sip:AN_date@ASTERISK_IP:ASTERISK_PORT"; } else if ($rU=="*2113") { # access to the "echo" service $ru = "sip:AN_echo@ASTERISK_IP:ASTERISK_PORT"; } else if ($rU=~"\*3[0-9]{3}") { # access to the conference service # remove the "*3" prefix and place the "CR_" prefix strip(2); prefix("CR_"); rewritehostport("ASTERISK_IP:ASTERISK_PORT"); } else { # unknown service $ru = "sip:AN_notavailable@ASTERISK_IP:ASTERISK_PORT"; } # after setting the proper RURI (to point to corresponding ASTERISK extension), # simply forward the call t_relay(); exit; } # ASTERISK HOOK - END # do lookup if (!lookup("location")) { # ASTERISK HOOK - BEGIN # callee is not registered, so different to Voicemail # First add the VM recording prefix to the RURI prefix("VMR_"); # forward to the call to Asterisk (replace below with real IP and port) rewritehostport("ASTERISK_IP:ASTERISK_PORT"); route(1); # ASTERISK HOOK - END exit; } # 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"); route(1); } route[1] { if (!t_relay()) { sl_reply_error(); }; exit; } failure_route[1] { if (t_was_cancelled()) { exit; } # if the failure code is "408 - timeout" or "486 - busy", # forward the calls to voicemail recording if (t_check_status("486|408")) { # ASTERISK HOOK - BEGIN # First revert the RURI to get the original user in RURI # Then add the VM recording prefix to the RURI revert_uri(); prefix("VMR_"); # forward to the call to Asterisk (replace below with real IP and port) rewritehostport("ASTERISK_IP:ASTERISK_PORT"); t_relay(); # ASTERISK HOOK - END exit; } } 1.9 How to use itAdd a SIP domain in the OpenSIPS server (note that the sip domain most point -via DNS- to your opensips server; also for a SIP domain, you can use the IP address of the SIP server too): $ mysql -h'localhost' -u'opensips' -p'opensipsrw' opensips > insert into domain (domain) values ("test.com"); $ opensipsctl fifo domain_reload Create some subscribers with your OpenSIPS platform (alice@test.com with SIP password '1234'): $ opensipsctl add alice@test.com 1234 $ opensipsctl add bob@test.com 4321 If you want to change the voicemail pin (for subscriber alice@test.com): $ mysql -h'localhost' -u'opensips' -p'opensipsrw' opensips > update subscriber set vm_password="6745" where username="alice" and domain="test.com"; Test voicemailRegister your alice subscriber (ex: using twinkle soft client) to your server. Keep bob unregistered. From alice dial bob address - you should get the prompt for leaving a voicemail messages/recording. Register bob also and dial *1111 - you should get the voicemail IVR and listen the recording left by alice. Test announcementsForm a registered subscriber, simply dial the service numbers as configured in OpenSIPS (see the beginning of the previous chapter). Test conferenceAdd a conference room to your system: $ mysql -h'localhost' -u'asterisk' -p'asterisk_pwd' asterisk > insert into meetme (confno, pin, adminpin) values ("761","1122","4322"); From a registered SIP user, dial *3761 (to dial in conf room 761) and at IVR prompt type 1122 access pin. |