******* IMPORTANT *************************************************************************** This is only needed if SER, the SIP Express Router, is used as SIP stack for SEMS. Since 1.0 you can simply use the sipctrl module, which provides a SIP stack for SEMS. sipctrl is set as the default control plugin, so usually there need not be any changes in that regard. But, if you need more functionality from the SIP stack, e.g. TLS, TCP, filtering of inbound requests etc., you can use ser2 with the binrpcctl module, and the SASI SER application server interface, and then parts of the information below apply. ********************************************************************************************* *************************** * Sems + Ser mini-HOWTO * *************************** 1. Introduction: This document describes the configuration of a typical Sems installation. In this document, it will be asserted that the reader has a basic understanding of Linux and SIP (Session Initiation Protocol, RFC 3261). If you don't, please try to get more familiar to Linux before continuing reading this document. If need more information on SIP, consider downloading iptel.org's SIP tutorial (www.iptel.org/sip). 2. Getting Ser and Sems to communicate together: To work properly, Sems needs a working Ser instance, which will be used as Sems' SIP stack. They communicate together through unix socket servers. Each side implements a server, enabling communication in both ways.You will need to configure Ser to use Sems's server, and Sems to use Ser's server. If you plan to run more than one Ser instance on the same host, be sure to know wich Ser instance you want to connect to SEMS. Usually the best is to have one SER instance which acts only as SIP stack for Sems. This SER could e.g. be listening on port 5070 for SIP messages. If in the home proxy (or any other place) a new INVITE should be sent to Sems, e.g. into a conference, the home proxy just relays the INVITE to the media server on port 5070. An example ser-sems.cfg file for this type of configuration can be found in core/etc/ser-sems.cfg. For each version of Sems there is a recommended version of SER to run it with. Thus it might be necessary to run two different versions of SER on the same machine. The best method to do this is to install the SER for Sems in a special location, e.g. in /opt/ser-sems/. For sems 0.10.0, for which the recommended ser version is ser-0.9.6-sems, Do : mkdir -p /opt/ser-sems/src cd /opt/ser-sems/src wget http://ftp.iptel.org/pub/sems/ser-0.9.6-sems_src.tar.gz tar -vxzf ser-0.9.6-sems_src.tar.gz cd ser-0.9.6-sems make install PREFIX=/opt/ser-sems This way, you have your own ser-sems installation in a compleatly seperate dir, and you can simply continue to play with it as you want :) 2.1 Configure Sems to use Ser's Unix socket server - edit Ser's configuration file (default location: /usr/local/etc/ser/ser.cfg) and look for the 'unix_sock=' configuration parameter. It should look like: unix_sock="/tmp/ser_sock" If the 'unix_sock' parameter does not exist, add it to the global section. If you have another value for that parameter, you don't need to change it, just remember it. - open Sems's configuration file (default location: /usr/local/etc/sems/sems.conf) - look for the 'ser_socket_name=' parameter. It must have the same value as 'unix_sock=' in Ser's configuration file, but without quotes. In our example: ser_socket_name=/tmp/ser_sock - configure the socket on which sems will get responses from ser: reply_socket_name=/tmp/sems_resp_sock - then configure SEMS' socket itself, useing the 'socket_name=' parameter in sems.conf. Default value: socket_name=/tmp/sems_sock 2.2 How to redirect a call to sems? 2.2.1 How it works: So that a caller gets connected to Sems, Ser has to pass every SIP message concerning that call to Sems through unix socket. Basically, Sems needs to get an INVITE message to begin a call, and a BYE message to terminate that call. Sems must also be aware of CANCELs. ACKs should be absorbed by Ser, as Sems does not need them. If you want SEMS to be aware of DTMF tones sent via SIP INFO messagees, they must be passed to SEMS as well. Redirecting the messages is a very simple operation. You just have to call the t_write_unix() function from the 'tm' module, which you have to load. As ser needs to be aware about the transaction of the request, you # need to call t_newtran() before writing the request to the socket using t_write_unix. 2.2.2 Load Ser's tm module - add following line to your ser.cfg in the 'module loading' section: loadmodule "/usr/local/lib/ser/modules/tm.so" configure tm to also pass provisional replies to sems: modparam("tm", "pass_provisional_replies", 1) 2.2.3 Redirecting SIP messages to Sems: In this example it is assumed that different applications should be executed when different LHS part of the URI are called, for example, 100 should be redirected to conference, 200 to announcement etc. - add following lines to your ser.cfg where you need to redirect the call: # select messages to redirect: if ( method=="ACK" || method=="INVITE" || method=="BYE" || method=="CANCEL" ){ # switch to stateful mode: if (!t_newtran()){ sl_send_reply("500","could not create transaction"); break; }; # prevent timeout on the other side: t_reply("100","Trying - just wait a minute !"); if (method=="INVITE"){ # redirect the call to the 'conference' plug-in # if the URI begin with 100 if (uri=~"sip:100.*@") { # assumes that Sems configuration parameter 'socket_name=' # has been set to /tmp/sems_sock if(!t_write_unix("/tmp/sems_sock","conference")) { t_reply("500","error contacting sems"); }; break; }; # redirect the call to the 'announcement' plug-in # if the URI begin with 200 if (uri=~"sip:200.*@") { if(!t_write_unix("/tmp/sems_sock","announcement")) { t_reply("500","error contacting sems"); }; break; }; # no service number, redirect to voicemail. # load email address into AVP so that voicemail gets the # callee's email address. (see below for avpops modparams) # avp_db_load( "$ruri", "$email/$email_scheme"); if(!t_write_unix("/tmp/sems_sock","voicemail")) { t_reply("500","error contacting sems"); }; break; } else if (method=="BYE" || method="CANCEL") { # Sems should already know which plug-in is handling that # call. "bye" is no plug-in name. It is a reserved name which # tells Sems to terminate the call. if(!t_write_unix("/tmp/sems_sock","bye")) { t_reply("500","error contacting sems"); }; } else if (method=="ACK") { # absorb ACKs t_relay(); } }; In more complicated scenario, you could also redirect REFERs to Sems which would enable you for example to implement a click-to-dial, or invite other users to take part to conference. 2.3 Passing additional parameters to SEMS: email address for voicemail Arbitrary AVPs (attribute value pairs) can be appended to a request when it is written to sems with t_write_req/t_write_unix using tw_append. The AVPs can e.g. be loaded from a database before, modified with avpops etc. This is a flexible method, e.g. the language of a user can be stored in the database, and the greeting and voicemail template will be selected according to the language. For the voicemail plug-in to work properly, sems has to get the email address of the callee. As email adresses are usually stored in a database and ser usually already has a db connection, the email address is looked up when the INVITE request is processed and added as extra header 'P-Email-Address' to the request. Then SEMS gets the address from this header. The relevant parts of the ser configuration then look like this: # load db module loadmodule "/usr/local/lib/ser/modules/mysql.so" # load avp modules loadmodule "modules/avp/avp.so" loadmodule "modules/avpops/avpops.so" # configure avpops db connection modparam( "avpops", "avp_url", "mysql://ser:heslo@localhost/ser" ) modparam( "avpops", "avp_table", "subscriber" ) modparam( "avpops", "uuid_column", "id" ) # configure aliases, the number doesn't matter as long as there are no collisions) modparam( "avpops", "avp_aliases", "email=i:67 ; language=i:68" ) # scheme to access the database modparam( "avpops", "db_scheme", "email_scheme:table=subscriber;value_col=email_address;value_type=string") modparam( "avpops", "db_scheme", "language_scheme:table=subscriber;value_col=language;value_type=string") # configure tm to append this when tw_appent voicemail_headers is used modparam("tm", "tw_append", "voicemail_headers:P-Email-Address=avp[$email];P-Language=avp[$language]") # ... # in route section: # no service number, redirect to voicemail. avp_db_load( "$ruri", "$email/$email_scheme"); avp_db_load( "$ruri", "$language/$language_scheme"); if(!t_write_unix("/tmp/sems_sock","voicemail/voicemail_headers")) { t_reply("500","error contacting sems"); }; break; This assumes that there is a column 'id', a column 'username', a column 'email_address' and a column 'language' in the table named 'subscriber'. dbtext sample file would be: ----- subscriber -------------------------------- id(int,auto) username(str,null) domain(str,null) email_address(str,null) language(str,null) 1:Alice:iptel.org:alice@mymail.org:english 2:rco:iptel.org:rco@iptel.org:french 3:Alex:iptel.org:alex@iptel.org:german ----- subsciber end ------------------------------ AVPs for email and language may also be loaded from e.g. raduis. 2.4 Passing SIP INFO messages to SEMS for DTMF The same mechanism using tw_append is used to pass the headers content-type, content-length and the body of an INFO message to SEMS: modparam( "tm", "tw_append", "info_append:hdr[Content-Length];hdr[Content-Type];msg[body]") ... if (method=="INFO") { if(!t_write_unix("/tmp/sems_sock","sems/info_append")){ log("could not contact sems\n"); t_reply("500","could not contact media server"); }; } 2.5 Passing arbitrary parameters to SEMS For early media announcements (early_announce app) the final reply code can be configured from the ser configuration. modparam( "avpops", "avp_aliases", "early_code=i:66 ; early_reason=i:67" ) # this is to be appended to the invite modparam("tm", "tw_append", "early_headers:P-Final-Reply-Code=avp[$early_code];P-Final-Reply-Reason=avp[$early_reason]") ... avp_write("405", "$early_code"); avp_write("Prohiboted", "$early_reason"); if(!t_write_unix("/tmp/sems_sock","early_announce/early_headers")){ log("could not contact early_announce\n"); t_reply("404","not found"); }; break; 3. Example real-life voicemail config This would be a real-life config example for voicemail with email address and language loading from radius, and NAT handling. modparam("tm", "tw_append", "vm_avps:P-Email-Address=$avp(CALLEE_EMAIL_AVP);P-Language=$avp(CALLEE_LANGUAGE_AVP)") ... if (method == "INVITE") { if (!t_newtran()) { xlog("L_ERR", "Could not create new transaction for <$rm> to <$ru>\n"); sl_send_reply("500","Could not create new transaction"); exit; }; t_reply("100", "Trying -- just wait a minute!"); if (isflagset(FROM_NATED)) { fix_nated_contact(); fix_nated_sdp("1"); # add direction=active }; if (isflagset(VOICEMAIL)) { avp_load_radius("callee"); if (!is_avp_set("$avp(CALLEE_EMAIL_AVP)")) { xlog("L_ERR", "Callee <$ru> email address not found\n"); t_reply("404", "Not found"); exit; }; if(!t_write_unix("/tmp/sems_sock","voicemail/vm_avps")) { xlog("L_ERR", "Could not contact Voicemail Server for <$ru>\n"); t_reply("500","Could not contact Voicemail Server"); exit; }; xlog("L_INFO", "Relayed $rm <$ru> by <$fu> to Voicemail Server\n"); exit; }; }; ... # In-dialog requests if (has_totag()) { loose_route(); if (isflagset(FROM_NATED)) { fix_nated_contact(); }; if ((method == "ACK") && (uri != myself)) { route(FIND_SERVICE); switch ($retcode) { case -1: xlog("L_ALERT", "Transit is not allowed to <$ru>\n"); sl_send_reply("403", "Forbidden - Transit is not allowed"); exit; default: }; if (!is_uri_host_local()) { xlog("L_ALERT", "Transit is not allowed to <$ru>\n"); sl_send_reply("403", "Forbidden - Transit is not allowed"); exit; }; # negative ack t_relay(); exit; }; if (uri != myself) { xlog("L_ALERT", "Transit is not allowed to <$ru>\n"); sl_send_reply("403", "Forbidden - Transit is not allowed"); exit; }; # Relay in-dialog request if (method =~ "(ACK|INVITE|BYE)") { if (!t_newtran()) { xlog("L_ERR", "Could not create new transaction\n"); sl_send_reply("500","Could not create new transaction"); exit; }; if (method == "INVITE") { if (isflagset(FROM_NATED)) fix_nated_sdp("1"); if(!t_write_unix("/tmp/sems_sock","invite")) { xlog("L_ERR", "Could not contact answer machine\n"); t_reply("500","Could not contact Answer Machine"); }; exit; }; if (method == "ACK") { t_relay(); xlog("L_INFO", "Relayed in-dialog ACK to <$ru>\n"); exit; }; if (method=="BYE") { if(!t_write_unix("/tmp/sems_sock","bye")) { xlog("L_ERR", "Could not contact answer machine\n"); t_reply("500","Could not contact Answer Machine"); }; xlog("L_INFO", "Relayed in-dialog BYE to <$ru>\n"); exit; }; };