4.6. Setting up SIP services

Operation of a SIP server is not always an easy task. Server administrators face many challenges of broken or misconfigured user agents, network and host failures, hostile attacks and other stress-makers. All such situations may lead to an operational failure. It is sometimes very difficult to figure out the root reason of a failure, particularly in a distributed environment with many SIP components involved. In this section, we share some of our practices and refer to tools which have proven to make the life of administrators easier.

4.6.1.1.1.

Message logging.

Frequently, operational errors are discovered or reported with a delay. Users frustrated by an error frequently approach administrators and scream "even though my SIP requests were absolutely OK yesterday, they were mistakenly denied by your server". If administrators do not record all SIP traffic at their site, they will not be able to identify the reason of the problem. We thus recommend that site operators record all messages passing their site and keep them stored for some period of time. They may use utilities such as ngrep or tcpdump .

4.6.1.1.2.

Real-time Traffic Watching.

Looking at SIP messages in real-time may help to gain understanding of problems. Though there are commercial tools available, using a simple, text-oriented tool such as ngrep is sufficient for the job, thanks to SIP's textual nature.

4.6.1.1.3.

Tracing Errors in Server Chains.

A request may pass any number of proxy servers on its path to its destination. If an error occurs in the chain, it is difficult for upstream troubleshooters and/or users complaining to administrators to learn more about error circumstances.

A nice utility for debugging server chains is sipsak, SIP Swiss Army Knife, traceroute-like tool for SIP developed at iptel.org. It allows you to send OPTIONS request with low, increasing Max-Forwards header-fields and follow how it propagates in SIP network. See its webpage at http://sipsak.berlios.de/ .

4.6.1.1.4.

Server Status Monitoring.

It is essential for solid operation to monitor server status continuously. We've been using two tools for this purpose. Sipsak does a great job of “pinging” a server, which may be used for alerting administrators of unresponsive servers.

Monit is a server watching utility which alerts administrators when a server dies.

4.6.1.1.5.

Dealing with DNS.

The SIP standard leverages DNS. Administrators of SIP servers should be aware of the impact of DNS on server's operation. A server's attempt to resolve an unresolvable address may block a server's process for a duration in the order of seconds. To be safer that the server does not stop responding due to being blocked by DNS resolving, we recommend the following practices:

  • Start a sufficient number of children processes. If one is blocked, the other children will keep serving.
  • Use DNS caching. For example, in Linux, there is an nscd daemon available for this purpose.
  • Process transactions statefuly if memory allows. That helps to absorb retransmissions without having to make DNS queries for each of them.

SIP Express Router (SER) is an industrial-strength, free VoIP server based on the Session Initiation Protocol (SIP, RFC3261). It is engineered to power IP telephony infrastructures up to large scale. The server keeps track of users, sets up VoIP sessions, relays instant messages and creates space for new plug-in applications. Its proven interoperability guarantees seamless integration with components from other vendors, eliminating the risk of a single-vendor trap. It has successfully participated in various interoperability tests along with products of other leading SIP vendors.

To install support for a MySQL database you will need to download the package ser-mysql, which is available from the same location from which you downloaded SIP Express Router. This package contains scripts to create the required database and establish permissions for preconfigured accounts. A recent release of MySQL is recommended, you should definitely use version higher than 4.0. Earlier versions may have problems with the syntax required to set permissions in the database.

If you do not already have a copy of MySQL installed, download it from http://www.mysql.com or check out your linux distribution. Many popular linux distribution come with the MySQL server pre-packaged.

Once you have the MySQL server installed and running, execute

/usr/sbin/ser_mysql.sh create

That will create database “ser” and all the tables that are required by SER.

You can verify that the database has been created, and correct permissions assigned by using the MySQL management tool and these steps:

Mysql> select * from user;
| Host               | User  | Password         | Select_priv | ...
| %                  | ser   | 4e633cf914a735a0 | N           | ... 
| localhost          | ser   | 4e633cf914a735a0 | Y           | ...
| %                  | serro | 7cb73a267cb7bd5f | N           | ...
| localhost          | serro | 7cb73a267cb7bd5f | Y           | ...

The above results show that the two users, ser and serro, have been created and granted the permissions needed to access the database. Note that in the above example the permissions have been modified to deny access to these accounts from any system(%) other than the local host.

mysql> connect ser;
Connection id:    294
Current database: ser

mysql> show tables;
+-----------------+
| Tables_in_ser   |
+-----------------+
| acc             |
| active_sessions |
| aliases         |
| config          |
| event           |
| grp             |
| location        |
| missed_calls    |
| pending         |
| phonebook       |
| reserved        |
| silo            |
| subscriber      |
| version         |
+-----------------+
14 rows in set (0.00 sec)

mysql> select * from subscriber;
| phplib_id                        | USERNAME | PASSWORD | FIRST_NAME | ...
| 4cefa7a4d3c8c2dbf6328520bd873a19 | admin     | heslo | first        | ...

The previous query shows that you have one user account defined and it has administrator privileges. Users with administrator privileges will be allowed to user the admin interface of Serweb.

We will need to add another account to be the administrator for your realm. We will do that later.

The configuration script, ser.cfg, is a part of every SER distribution and defines default behaviour of the server. It allows users to register with the server and have requests proxied to other users registered at the same server as well as to other SIP servers.

After performing routine checks, the script looks whether an incoming request is for the served domain (administrative domain). If this is true and the request is “REGISTER”, SER acts as a SIP registrar and updates the user location database. Optionally, it verifies user's identity first to avoid unauthorized contact manipulation.

Non-REGISTER requests for served domains are then processed using the user location database. If a contact is found for a Requested-URI, script execution proceeds to stateful forwarding, a negative 404 reply is generated otherwise. Requests targeted outside the served domain are always statefuly forwarded.

Note that the default configuration settings as set by this simple script have several limitations:

Example 4.3. Default Configuration Script

#
# simple quick-start config script
#

# ----------- global configuration parameters ------------------------

debug=3         # debug level (cmd line: -dddddddddd)
fork=yes
log_stderror=no	# (cmd line: -E)

/* Uncomment these lines to enter debugging mode 
fork=no
log_stderror=yes
*/

check_via=no	# (cmd. line: -v)
dns=no           # (cmd. line: -r)
rev_dns=no      # (cmd. line: -R)
port=5060
children=4
fifo="/tmp/ser_fifo"

# ------------------ module loading ----------------------------------

# Uncomment this if you want to use SQL database
#loadmodule "/usr/local/lib/ser/modules/mysql.so"

loadmodule "/usr/local/lib/ser/modules/sl.so"
loadmodule "/usr/local/lib/ser/modules/tm.so"
loadmodule "/usr/local/lib/ser/modules/rr.so"
loadmodule "/usr/local/lib/ser/modules/maxfwd.so"
loadmodule "/usr/local/lib/ser/modules/usrloc.so"
loadmodule "/usr/local/lib/ser/modules/registrar.so"
loadmodule "/usr/local/lib/ser/modules/textops.so"

# Uncomment this if you want digest authentication
# mysql.so must be loaded !
#loadmodule "/usr/local/lib/ser/modules/auth.so"
#loadmodule "/usr/local/lib/ser/modules/auth_db.so"

# ----------------- setting module-specific parameters ---------------

# -- usrloc params --

modparam("usrloc", "db_mode",   0)

# Uncomment this if you want to use SQL database 
# for persistent storage and comment the previous line
#modparam("usrloc", "db_mode", 2)

# -- auth params --
# Uncomment if you are using auth module
#
#modparam("auth_db", "calculate_ha1", yes)
#
# If you set "calculate_ha1" parameter to yes (which true in this config), 
# uncomment also the following parameter)
#
#modparam("auth_db", "password_column", "password")

# -- rr params --
# add value to ;lr param to make some broken UAs happy
modparam("rr", "enable_full_lr", 1)

# -------------------------  request routing logic -------------------

# main routing logic

route{

	# initial sanity checks -- messages with
	# max_forwards==0, or excessively long requests
	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483","Too Many Hops");
		break;
	};
	if (msg:len >=  max_len ) {
		sl_send_reply("513", "Message too big");
		break;
	};

	# we record-route all messages -- to make sure that
	# subsequent messages will go through our proxy; that's
	# particularly good if upstream and downstream entities
	# use different transport protocol
	if (!method=="REGISTER") record_route();	

	# subsequent messages withing a dialog should take the
	# path determined by record-routing
	if (loose_route()) {
		# mark routing logic in request
		append_hf("P-hint: rr-enforced\r\n"); 
		route(1);
		break;
	};

	if (!uri==myself) {
		# mark routing logic in request
		append_hf("P-hint: outbound\r\n"); 
		route(1);
		break;
	};

	# if the request is for other domain use UsrLoc
	# (in case, it does not work, use the following command
	# with proper names and addresses in it)
	if (uri==myself) {

		if (method=="REGISTER") {

# Uncomment this if you want to use digest authentication
#			if (!www_authorize("iptel.org", "subscriber")) {
#				www_challenge("iptel.org", "0");
#				break;
#			};

			save("location");
			break;
		};

		lookup("aliases");
		if (!uri==myself) {
			append_hf("P-hint: outbound alias\r\n"); 
			route(1);
			break;
		};

		# native SIP destinations are handled using our USRLOC DB
		if (!lookup("location")) {
			sl_send_reply("404", "Not Found");
			break;
		};
	};
	append_hf("P-hint: usrloc applied\r\n"); 
	route(1);
}

route[1] 
{
	# send it out now; use stateful forwarding as it works reliably
	# even for UDP2TCP
	if (!t_relay()) {
		sl_reply_error();
	};
}

Many services depend on the status of messages relayed downstream: forward on busy and forward on no reply to name the two most well-known. To support implementation of such services, SER allows to return to request processing when forwarding of a request fails. When a request is reprocessed, new request branches may be initiated or the transaction can be completed at the discretion of the script writer.

The primitives used are t_on_failure(r) and failure_route[r]{}. If t_on_failure action is called before a request is statefuly forwarded and a forwarding failure occurs, SER will return to request processing in a failure_route block. Failures include: receipt of a SIP error (status code >= 300 ) from downstream and the absence of a final reply within the final response period.

The duration of the timer is governed by parameters of the tm module. fr_timer is the duration of the timer set for non-INVITE transactions and INVITE transactions for which no provisional response is received. If the timer hits, it indicates that a downstream server is unresponsive. fr_inv_timer governs time to wait for a final reply for an INVITE. It is typically longer than fr_timer because final reply may take a long time until the callee (finds a mobile phone in his pocket and) answers the call.

In Example 4.5, failure_route[1] is set to be entered on error using the t_on_failure(1) action. Within this reply block, SER is instructed to initiate a new branch and try to reach the called party at another destination (sip:nonsense@iptel.org). To deal with the case when none of the alternate destinations succeed, t_on_failure is set again. If this case really occurs, failure_route[2] is entered and a last resort destination (sip:foo@iptel.org) is tried.

Example 4.5. On-Reply Processing

#
# example script showing both types of forking;
# incoming message is forked in parallel to
# 'nobody' and 'parallel', if no positive reply
# appears with final_response timer, nonsense
# is retried (serial forking); than, destination
# 'foo' is given last chance

# ------------------ module loading ----------------------------------

loadmodule "modules/sl/sl.so"
loadmodule "modules/tm/tm.so"

# ----------------- setting module-specific parameters ---------------

# -- tm params --
# set time for which ser will be waiting for a final response;
# fr_inv_timer sets value for INVITE transactions, fr_timer
# for all others
modparam("tm", "fr_inv_timer", 15 )
modparam("tm", "fr_timer", 10 )

# -------------------------  request routing logic -------------------

# main routing logic

route{
	# for testing purposes, simply okay all REGISTERs
	if (method=="REGISTER") {
		log("REGISTER");
		sl_send_reply("200", "ok");
		break;
	};
	# try these two destinations first in parallel; the second
	# destination is targeted to sink port -- that will make ser
	# wait until timer hits
	seturi("sip:nobody@iptel.org");
	append_branch("sip:parallel@iptel.org:9");
	# if we do not get a positive reply, continue at reply_route[1]
	t_on_failure("1");
	# forward the request to all destinations in destination set now 
	t_relay();
}

failure_route[1] {
	# forwarding failed -- try again at another destination 
	append_branch("sip:nonsense@iptel.org");
	log(1,"first redirection\n");
	# if this alternative destination fails too, proceed to ... 
	t_on_failure("2");
	t_relay();
}

failure_route[2] {
	# try out the last resort destination
	append_branch("sip:foo@iptel.org");
	log(1, "second redirection\n");
	# we no more call t_on_negative here; if this destination
	# fails too, transaction will complete
	t_relay();
}

In some scenarios, like termination of calls in the PSTN, SIP administrators may wish to keep track of placed calls. SER can be configured to report on completed transactions. Reports are sent by default to syslog facility. Support for RADIUS and MySQL accounting exists as well.

Note that SER is by no means call-stateful. It reports on completed transactions, i.e., after a successful call set up is reported, it drops any call-related state. When a call is terminated, a transactional state for the BYE request is created and forgotten again after the transaction completes. This is a feature and not a bug -- keeping the state information during transactions only allows to achieve significantly higher scalability. It is then up to the accounting application to correlate call initiation and termination events.

To enable call accounting, tm and acc modules need to be loaded, requests need to be processed statefuly and labeled for accounting. This means that if you want a transaction to be reported, the initial request must have taken the path "setflag(X), t_relay" in the SER script. X must have the value configured in the acc_flag configuration option.

Also note, that by default only transactions that initiate a SIP dialog (typically INVITE) visit a proxy server. Subsequent transactions are exchanged directly between end-devices, do not visit proxy server and cannot be reported. To be able to report on subsequent transactions, you need to force them to visit the proxy server by turning record routing on.

Example 4.6. Configuration with Enabled Accounting

#
# example: accounting calls to numerical destinations
#

# ------------------ module loading ----------------------------------

loadmodule "modules/tm/tm.so"
loadmodule "modules/acc/acc.so"
loadmodule "modules/sl/sl.so"
loadmodule "modules/maxfwd/maxfwd.so"
loadmodule "modules/rr/rr.so"

# ----------------- setting module-specific parameters ---------------

# -- acc params --
# set the reporting log level
modparam("acc", "log_level", 1)
# number of flag, which will be used for accounting; if a message is
# labeled with this flag, its completion status will be reported
modparam("acc", "log_flag", 1 )

# -------------------------  request routing logic -------------------

# main routing logic

route{

	/* ********* ROUTINE CHECKS  ********************************** */

	# filter too old messages
	if (!mf_process_maxfwd_header("10")) {
		log("LOG: Too many hops\n");
		sl_send_reply("483","Too Many Hops");
		break;
	};
	if (len_gt( max_len )) {
		sl_send_reply("513", "Wow -- Message too large");
		break;
	};

    #  Process record-routing
    if (loose_route()) { t_relay(); break; };


	# labeled all transaction for accounting
	setflag(1);

	# record-route INVITES to make sure BYEs will visit our server too
	if (method=="INVITE") record_route();

	# forward the request statefuly now; (we need *stateful* forwarding,
	# because the stateful mode correlates requests with replies and
	# drops retransmissions; otherwise, we would have to report on
	# every single message received)
	if (!t_relay()) {
		sl_reply_error(); 
		break; 
	};

}

SER can report missed calls via the syslog facility or to MySQL. Mysql reporting can be utilized by SER's complementary web interface, Serweb.

Reporting of missed calls is enabled by the acc module. There are two cases, on which you want to report. The first case is when a callee is offline. The other case is when a user is online, but call establishment fails. There may be many failure reasons (call cancellation, inactive phone, busy phone, server timer, etc.), all of them leading to a negative (>=300) reply sent to the caller. The acc module can be configured to issue a missed-call report whenever a transaction completes with a negative status.

The following configuration fragment reports a missed call in both cases. The top half of the condition reports on calls missed due to offline callee status, using the acc_request action. The action is wrapped in transactional processing (t_newtran) to guarantee that reports are not duplicated on receipt of retransmissions.

The bottom half of the condition marks transactions to online users in order to be reported on failure. That is what the setflag(3) action is responsible for, along with the configuration option log_missed_flag. This option configures SER to report on all transactions, which were marked with flag 3.

loadmodule("modules/tm/tm.so");
loadmodule("modules/acc/acc.so");
....
# if a call is labeled using setflag(3) and is missed, it will
# be reported
...
modparam("acc", "log_missed_flag", 3 );
if (!lookup("location")) {
     # call invitations to off-line users are reported using the
     # acc_request action; to avoid duplicate reports on request
     # retransmissions, request is processed statefuly (t_newtran,
     # t_reply)
     if ((method=="INVITE" || method=="ACK") && t_newtran() ) {
          t_reply("404", "Not Found");
	  acc_request("404 Not Found");
          break;
     };
     # all other requests to off-line users are simply replied
     # statelessly and no reports are issued
    sl_send_reply("404", "Not Found");
    break;
} else {
     # user on-line; report on failed transactions; mark the
     # transaction for reporting using the same number as 
     # configured above; if the call is really missed, a report
     # will be issued
     setflag(3);
     # forward to user's current destination
     t_relay();
     break;
};

Frequently, it is desirable for a user to have multiple addresses in a domain. For example, a user with username “john.doe” wants to be reachable at a shorter address “john” or at a numerical address “12335”, so that PSTN callers with numeric-only key-pads can reach him as well.

With SER, you can maintain a special user location table and translate existing aliases to canonical usernames using the lookup action from the usrloc module. The following script fragment demonstrates the use of lookup for this purpose.

The table with aliases is updated using the serctl tool. The command serctl alias add <alias> <uri> adds a new alias, the command serctl alias show <user> prints an existing alias, and the command serctl alias rm <user> removes it.

[jiri@cat sip_router]$ serctl alias add 1234 sip:john.doe@foo.bar
sip:john.doe@foo.bar
200 Added to table
('1234','sip:john.doe@foo.bar') to 'aliases'
[jiri@cat sip_router]$ serctl alias add john sip:john.doe@foo.bar
sip:john.doe@foo.bar
200 Added to table
('john','sip:john.doe@foo.bar') to 'aliases'
[jiri@cat sip_router]$ serctl alias show john                    
<sip:john.doe@foo.bar>;q=1.00;expires=1073741811
[jiri@cat sip_router]$ serctl alias rm john  
200 user (aliases, john) deleted				

Note that the persistence of records needs to be turned on in the usrloc module. All changes to aliases would be otherwise lost on server reboot. To enable the persistence, set the db_mode usrloc parameter to a non-zero value.

# ....load module ...
loadmodule "modules/usrloc/usrloc.so"
# ... turn on persistence -- all changes to user tables are immediately
# flushed to mysql
modparam("usrloc", "db_mode",   1)
# the SQL address:
modparam("usrloc", "db_url","mysql://ser:secret@dbhost/ser")

There are two tasks related to the management of SIP users: maintaining user accounts and maintaining user contacts. Both these jobs can be done using the serctl command-line tool. The complimentary web interface, Serweb, can be used for this purpose as well.

If user authentication is turned on, which is highly advisable, user accounts must be created before users can log in. To create a new user account, use the serctl add utility with the username, password and email as parameters. It is important that the environment variable SIP_DOMAIN is set to your domain and matches the realm values used in your script. The realm value is used for calculation of credentials stored in the subscriber database, which are bound permanently to this value.

[jiri@cat gen_ha1]$ export SIP_DOMAIN=foo.bar
[jiri@cat gen_ha1]$ serctl add newuser secret newuser@foo.bar
MySql Password: 
new user added

serctl can also change the user's password or remove existing accounts from the system permanently.

[jiri@cat gen_ha1]$ serctl passwd newuser newpassword
MySql Password: 
password change succeeded
[jiri@cat gen_ha1]$ serctl rm newuser                
MySql Password: 
user removed

User contacts are typically automatically uploaded by SIP phones to the server during the registration process and administrators do not need to worry about them. However, users may wish to append permanent contacts to PSTN gateways or to locations in other administrative domains. To manipulate the contacts in such cases, use the serctl ul tool. Note that this is the only correct way to update contacts -- direct changes of the back-end MySQL database do not affect server's memory. Also note, that if persistence is turned off (usrloc “db_mode” parameter set to “0”), all contacts will be lost on server reboot. Make sure that the persistence is enabled if you add permanent contacts.

To add a new permanent contact for a user, call serctl ul add <username> <contact>. To delete all user's contacts, call serctl ul rm <username>. The command serctl ul show <username> prints all current contacts of this user.

[jiri@cat gen_ha1]$ serctl ul add newuser sip:666@gateway.foo.bar
sip:666@gateway.foo.bar
200 Added to table
('newuser','sip:666@gateway.foo.bar') to 'location'
[jiri@cat gen_ha1]$ serctl ul show newuser
<sip:666@gateway.foo.bar>;q=1.00;expires=1073741812
[jiri@cat gen_ha1]$ serctl ul rm newuser  
200 user (location, newuser) deleted
[jiri@cat gen_ha1]$ serctl ul show newuser
404 Username newuser in table location not found

It is often important to exercise some sort of access control. A typical case is when SER is used to guard a PSTN gateway. If a gateway could not be well guarded, unauthorized users would be able to use it to make calls to the PSTN, inflicting high costs.

There are few issues you need to understand when configuring SER for this purpose. First, if a gateway is built or configured to accept calls from anywhere, callers may easily bypass your access control server and communicate with the gateway directly. You then need to enforce at transport layer that signaling is only accepted if coming via SER and deny SIP packets coming from other hosts and port numbers. Your network must be configured not to allow forged IP addresses. Also, you need to turn on record routing to assure that all session requests will travel via SER. Otherwise, caller's devices would send subsequent SIP requests directly to your gateway, which would fail because of transport filtering.

Authorization (i.e., the process of determining who may call where) is facilitated in SER using the group membership concept. Scripts make decisions on whether a caller is authorized to make a call to a specific destination based on the user's membership in a group. For example, a policy may be set up to allow calls to international destinations only to users, who are members of “int” group. Before user's group membership is checked, his identity must be verified. Without cryptographic verification of the user's identity, it would be impossible to confirm that a caller really is who he claims to be.

The following script demonstrates, how to configure SER as an access control server for a PSTN gateway. The script verifies user identity using digest authentication, checks user's privileges, and forces all requests to visit the server.

Example 4.8. Script for Gateway Access Control

loadmodule "modules/sl/sl.so"
loadmodule "modules/tm/tm.so"
loadmodule "modules/acc/acc.so"
loadmodule "modules/rr/rr.so"
loadmodule "modules/maxfwd/maxfwd.so"
loadmodule "modules/mysql/mysql.so"
loadmodule "modules/auth/auth.so"
loadmodule "modules/auth_db/auth_db.so"
loadmodule "modules/group/group.so"
loadmodule "modules/uri/uri.so"

# ----------------- setting module-specific parameters ---------------

modparam("auth_db", "db_url","mysql:ser:heslo@localhost/ser")
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")

# -- acc params --
modparam("acc", "log_level", 1)
# that is the flag for which we will account -- don't forget to
# set the same one :-)
modparam("acc", "log_flag", 1 )

# -------------------------  request routing logic -------------------

# main routing logic

route{

    /* ********* ROUTINE CHECKS  ********************************** */

    # filter too old messages
    if (!mf_process_maxfwd_header("10")) {
        log("LOG: Too many hops\n");
        sl_send_reply("483","Too Many Hops");
        break;
    };
    if (len_gt( max_len )) {
        sl_send_reply("513", "Wow -- Message too large");
        break;
    };

    /* ********* RR ********************************** */

    /* grant Route routing if route headers present */
    if (loose_route()) { t_relay(); break; };
    
    /* record-route INVITEs -- all subsequent requests must visit us */
    if (method=="INVITE") {
        record_route();
    };

# now check if it really is a PSTN destination which should be handled
# by our gateway; if not, and the request is an invitation, drop it --
# we cannot terminate it in PSTN; relay non-INVITE requests -- it may
# be for example BYEs sent by gateway to call originator
    if (!uri=~"sip:\+?[0-9]+@.*") {
        if (method=="INVITE") {
            sl_send_reply("403", "Call cannot be served here");
        } else {
            forward(uri:host, uri:port);
        };
        break;
    }; 

    # account completed transactions via syslog
    setflag(1);

    # free call destinations ... no authentication needed
    if ( is_user_in("Request-URI", "free-pstn")  /* free destinations */
            |  uri=~"sip:[79][0-9][0-9][0-9]@.*"  /* local PBX */
            | uri=~"sip:98[0-9][0-9][0-9][0-9]") {
        log("free call");
    } else if (src_ip==192.168.0.10) {
    # our gateway does not support digest authentication;
    # verify that a request is coming from it by source
    # address
        log("gateway-originated request");
    } else {
    # in all other cases, we need to check the request against
    # access control lists; first of all, verify request
    # originator's identity

        if (!proxy_authorize(    "gateway" /* realm */,
            "subscriber" /* table name */))  {
        proxy_challenge( "gateway" /* realm */, "0" /* no qop */ );
        break;
        };

    # authorize only for INVITEs -- RR/Contact may result in weird
    # things showing up in d-uri that would break our logic; our
    # major concern is INVITE which causes PSTN costs 

        if (method=="INVITE") {

        # does the authenticated user have a permission for local
        # calls (destinations beginning with a single zero)? 
        # (i.e., is he in the "local" group?)
        if (uri=~"sip:0[1-9][0-9]+@.*") {
            if (!is_user_in("credentials", "local")) {
                sl_send_reply("403", "No permission for local calls"); 
                break;
            };
        # the same for long-distance (destinations begin with two zeros")
        } else if (uri=~"sip:00[1-9][0-9]+@.*") {
            if (!is_user_in("credentials", "ld")) {
                sl_send_reply("403", " no permission for LD ");
                break;
            };
        # the same for international calls (three zeros)
        } else if (uri=~"sip:000[1-9][0-9]+@.*") {
            if (!is_user_in("credentials", "int")) {
                sl_send_reply("403", "International permissions needed");
                break;
            };
        # everything else (e.g., interplanetary calls) is denied
        } else {
            sl_send_reply("403", "Forbidden");
            break;
        };

        }; # INVITE to authorized PSTN

    }; # authorized PSTN

    # if you have passed through all the checks, let your call go to GW!

    rewritehostport("192.168.0.10:5060");

    # forward the request now
    if (!t_relay()) {
        sl_reply_error(); 
        break; 
    };

}

Use the serctl tool to maintain group membership. The command serctl acl grant <username> <group> makes a user member of a group, the command serctl acl show <username> shows groups of which a user is member, and the command serctl acl revoke <username> [<group>] revokes a user's membership in one or all groups.

[jiri@cat sip_router]$ serctl acl grant john int
MySql Password: 
+------+-----+---------------------+
| user | grp | last_modified       |
+------+-----+---------------------+
| john | int | 2002-12-08 02:09:20 |
+------+-----+---------------------+

In this section we will describe an example configuration of the Asterisk PBX. We will focus mainly on the configuration of the SIP part.

The configuration files can be found in the /etc/asterisk directory. The most important files are: sip.conf which contains configuration of SIP user agent and extensions.conf which defines dialing plan.

In our simple example we will configure asterisk to act as a simple back-to-back user agent. It will allow SIP user agents to register to it and make calls which will be routed to an outbound proxy.

The file sip.conf contains the following settings:

;
; SIP Configuration for Asterisk
;
[general]
port = 5060                     ; Port to bind to
bindaddr = 0.0.0.0              ; Address to bind to
context = from-sip              ; Default for incoming calls
;
register => asterisk:password@iptel.org/jan     ; Register with a SIP provider

[iptel]
type=friend
username=asterisk
secret=password
fromdomain=iptel.org
host=iptel.org

[jan]
type=friend
username=jan
;secret=blah
host=dynamic
canreinvite=no

Section [general] contains some generic settings. We configure asterisk to listen on port 5060 and to listen on all available interfaces. We also specify context to be “from-sip”. The same context must be later configured in extensions.conf !

The line beginning with “register” instructs asterisk to act as a user agent and register with the iptel.org server as user “asterisk” with password “password”. The /jan part indicates that all incoming calls to user asterisk will be forwarded to user “jan” registered at the asterisk server.

Section [iptel] contains configuration of a peer -- in this case it is the iptel.org proxy server, because we will be using this server as an outbound proxy. In this section we specify the parameter “fromdomain” because we want all outgoing messages to have this domain in the “From” header field.

The last section [jan] contains credentials and data for a user that will be able to register with the asterisk server. In this case we will configure one SIP phone with username “jan” and with an empty password and with a phone which will be registered with the asterisk server to receive calls for username “jan”.

The file extensions.conf contains the following settings:

[from-sip]
exten => jan,1,Dial(SIP/jan)
exten => jan,2,Hangup
exten => _3.,1,SetCallerID(jan)
exten => _3.,2,SetCIDName(Jan Janak)
exten => _3.,3,Dial(SIP/${EXTEN:1}@iptel)
exten => _3.,4,Playback(invalid)
exten => _3.,5,Hangup

The first line describes the context which we have configured in sip.conf already. The following lines describe the dialing plan. The “exten” directive means that the extension of the call will be processed. The first parameter after the => is the extension. If the extension starts with an underscore then it will be treated as an expression, otherwise only exact match will be accepted.

In our example, the first two lines match the extension “jan”. The rest of the lines will match any extension starting with digit 3.

The second parameter is the preference of the line in the dialing plan. The last parameter is the action to be executed when the extension matches. The first line says that calls with extension “jan” will be routed to SIP and peer “jan”.

Any extensions beginning with 3 will be routed to the iptel.org server using SIP and username “jan” will be set as the caller ID. If a call fails then asterisk will reply with an error message.

The Vovida Open Communication Application Library (VOCAL) is an open source project targeted at facilitating the adoption of Voice over IP in the marketplace. VOCAL provides the development community with software and tools needed to build Voice over IP features, applications and services. The VOCAL system is a distributed network of servers that provides Voice Over Internet Protocol (Voice over IP) telephony services. VOCAL supports devices that communicate using the Session Initiation Protocol (SIP, RFC3261). VOCAL also supports analog telephones via residential gateways. VOCAL supports on-network and off-network calling. Off-network calling enables subscribers to connect to parties through either the Internet or the Public Switched Telephone Network (PSTN).

The basic software in VOCAL includes a set of SIP based servers (Redirect Server, Feature Server, Provisioning Server and Marshal Proxy Server). This is the stable development branch of the VOCAL server. Moreover, even if the following applications are not included in the current release (1.5.0), their source code remains available in the CVS archive http://www.vovida.org/cgi-bin/fom?file=556.

  • SIP to MGCP translator
  • Policy server
  • Conference proxy server
  • SIP to H.323 translator
  • JTAPI feature server
  • SIP User Agent (replaced by SIPset)
  • SNMP/NetMgnt

For a more detailed overview of VOCAL system please refer to http://www.vovida.org

If you have a previous installation of VOCAL that uses the “vocalstart” executable to run, you must stop all servers with “vocalstart stop”. Note that “vocalstart” is no longer used as of version 1.4.0. In order to perform this action and to install VOCAL you must be logged in as root in your Linux system.

There are two options for downloading and installing VOCAL:

  • Installing from source:

    Type the following sequence of commands:

    ./configure
    make
    make install
    

    You must become root before executing make install.

If you want to have more information about compiling and installing VOCAL please refer to the file BUILD.txt.

To set up a basic configuration you have to use the configuration script indicated here:

/usr/local/vocal/bin/allinoneconfigure/allinoneconfigure

Running the “allinoneconfigure” script, you will be asked a number of questions. For basic services setup answer all questions with the default answers.

After such a default configuration an Apache Web Server has been reconfigured on your Linux machine to provide basic web-based configuration (provisioning in VOCAL terms) and must be restarted for this to take effect.

In order to restart the Apache Web Server you should run:

/etc/rc.d/init.d/httpd restart

When the Apache Web Server is restarted you will be able to use the web-based provisioning of the VOCAL system. In order to start provisioning your system you will have to point a web browser to.

http://your.server.name/vocal/

You will be prompted for a password. The username is “vocal”. During configuration, you were asked to enter a password or choose to have one generated for you. If you have forgotten the password from that step, you can regenerate one by running the command:

allinoneconfigure -r

After you have run the allinoneconfigure script, make sure that your VOCAL system is running typing the following command:

/usr/local/vocal/bin/vocalctl status

You have to make sure that you are able to see all of the necessary processes, as follows:

fs 5080	14957
fs 5085	14955
ms 5060	15002
...

If instead of such a list of actively running servers with the details of the ports they are listening to, you see “vocald is not running”, then your VOCAL system is not running because something went wrong in the configuration.

If your VOCAL system is running, you can verify your installation by running the “verifysip” command.

Passing it the -a option causes “verifysip” to create two test users, test1000 and test1001, and make a call from test1000 to test1001. After testing, “verifysip” will remove the two users. You should be able to run it with a command like this:

/usr/local/vocal/bin/verifysip -a

If your installation is OK, you should see the following text:

VOCAL basic call test passed.