INRIA - SIP.edu deployment notes

Philippe Sultan

Revision History
Revision $Revision: 1.2 $$Date: 2006/09/22 11:15:39 $

Table of Contents
1. Copyright and license
2. Introduction
3. Overview
4. Configuring OpenSER
4.1. RADIUS authentication and accounting
4.2. Call processing
5. GNU Free Documentation License

1. Copyright and license

Copyright (c) 2006 Philippe Sultan, INRIA. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".


2. Introduction

INRIA has joined Internet2 SIP.edu initiative to offer its users the ability to be joined from a growing SIP network, and to help them getting familiar with VoIP tools.

About 5000 email addresses and PBX extensions are reachable from the Internet. This covers the users of every Research Units (located in different french cities) that are part of INRIA.


3. Overview

This picture gives an general view of the software and hardware set up to match the requirements of the SIP.edu architecture.

Figure 1. SIP architecture

Main elements description

SIP proxy/registrar

The OpenSER SIP proxy/registrar is responsible for the inria.fr zone. A script that retrieves regular INRIA phone numbers from the LDAP directory comes along with this server. REGISTER requests are processed locally, regular INVITE requests are forwarded to Asterisk.

IPBX Asterisk

The Asterisk server manages a small set of SIP phones. It is also in charge of processing the INVITE requests relayed by OpenSER, and forward them to the SIP - PSTN gateway.

RADIUS server

The FreeRADIUS server is dedicated to softphone user authentication. Depending on the resource that is being called, user authentication may or may not be triggered.

Directory

INRIA's members are registered in an OpenLDAP directory.


4. Configuring OpenSER

OpenSER handles calls originating from the Internet. As of version 1.0.1, OpenSER's configuration file does not show noticeable differences from SER's, for our simple use. Call processing logic is stored in a single configuration file, detailed hereafter.

OpenSER's configuration file is divided into four main sections: global parameters, external module loading, module parameters and routing blocks, which contain request routing logic. SIP.edu's cookbook (thanks for this!) provides extensive information regarding SER's installation and configuration, that also apply to our version of OpenSER. Therefore, only the routing logic and RADIUS configuration are detailed in these notes. The link to the cookbook : http://mit.edu/sip/sip.edu/ser.shtml


4.1. RADIUS authentication and accounting

OpenSER can rely on an external RADIUS server for authentication and accounting purposes. radiusclient-ng must be installed on the system to have this RADIUS client feature working. For more information about how to integrate RADIUS with OpenSER : http://www.openser.org/docs/openser-radius-1.0.x.html

As shown in the module loading configuration section in openser.cfg, both auth, auth_radius and acc modules must be loaded on OpenSER startup :


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

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

loadmodule "/usr/local/lib/openser/modules/maxfwd.so"
loadmodule "/usr/local/lib/openser/modules/sl.so"
loadmodule "/usr/local/lib/openser/modules/rr.so"
loadmodule "/usr/local/lib/openser/modules/registrar.so"
loadmodule "/usr/local/lib/openser/modules/tm.so"
loadmodule "/usr/local/lib/openser/modules/usrloc.so"
loadmodule "/usr/local/lib/openser/modules/textops.so"
loadmodule "/usr/local/lib/openser/modules/uri.so"
loadmodule "/usr/local/lib/openser/modules/uri_db.so"
loadmodule "/usr/local/lib/openser/modules/auth.so"
loadmodule "/usr/local/lib/openser/modules/auth_radius.so"
loadmodule "/usr/local/lib/openser/modules/acc.so"
loadmodule "/usr/local/lib/openser/modules/exec.so"

The module-specific parameters settings section, mainly to indicate the location of configuration file for radiusclient-ng.


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

# -- acc params --
# set the reporting log level
modparam("acc", "radius_flag", 1)
modparam("acc", "radius_missed_flag", 2)
modparam("acc", "radius_config", "/usr/local/etc/radiusclient-ng/radiusclient.conf")

# -- auth_radius params --
modparam("auth_radius", "radius_config", "/usr/local/etc/radiusclient-ng/radiusclient.conf")
modparam("auth_radius", "service_type", 15)

4.2. Call processing

This section covers the call processing section in openser.cfg. The corresponding section, named request routing logic is splitted and analyzed here.

General rules for call processing

Request sanity check

Simple request check is done, as well as setting accounting flags. Comments in the script explicitly mention the taken actions.


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

# main routing logic

route{
	/* ********* ROUTINE CHECKS  ********************************** */

	# 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");
		return;
	};
	if (msg:len >=  2048 ) {
		sl_send_reply("513", "Message too big");
		return;
	};
	
	# set flag for Radius Accounting:
        if (!method=="OPTIONS") setflag(3); 

        if (method=="INVITE") {
                log(1, "INVITE MESSAGE RECEIVED - START ACC\n");
                setflag(1); /* set for accounting (the same value as in log_flag!) */
		setflag(2);
        };

        if (method=="BYE") {
                log (1, "BYE  - STOP ACCOUNTING\n");
                setflag(1);
        };

        if (method=="CANCEL") {
                log (1, "CANCEL - STOP ACCOUNTING\n");
                setflag(1);
        };

	# subsequent messages withing a dialog should take the
	# path determined by record-routing
	if (loose_route()) {
		route(1);
		return;
	};

	# 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();	


	lookup("aliases");

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

REGISTER requests are processed by OpenSER, authentication is tested against FreeRADIUS


	# 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 (method=="REGISTER") {

	# Uncomment this if you want to use digest authentication
		if (!radius_www_authorize("inria.fr")) {
			www_challenge("inria.fr", "0");
			return;
		};
		save("location");
		return;
	};
OpenSER's registration database check

First, OpenSER checks its location database in order to forward the INVITE request to a registered SIP user. SIP users from INRIA can only register non numeric URIs that take the form of firstname.lastname@inria.fr.


	# native SIP destinations are handled using our USRLOC DB
	# In case a user is not found, we dig into INRIA's LDAP directory
	if (lookup("location")) {
		# user found -- forward to him and label the request
		append_hf("P-hint: USRLOC\r\n");
		forward(uri:host,uri:port);
	} else {
LDAP search

If this fails, it will dig into the LDAP directory to find out a regular phone number that matches the SIP URI, and forward the INVITE request to Asterisk (route block [3] in openser.cfg). Route block [3] forwards INVITE requests to Asterisk without authentication, thus allowing any SIP.edu member to call INRIA's extensions. After this step, the destination URI value is either numeric (eg. a PSTN number) or null.


		if (exec_dset("/usr/local/etc/ser/sipldap")) {
		   log(1," sipldap call"); 
		   route(3);
		   return;
		}
Calling a non INRIA extension

If the user wants to call a non INRIA extension number, route block [4] is called. User authentication is triggered, and checked against FreeRADIUS


		# now check for destinations through the gateway. 15, 17 and 18
		# are always sent to the gateway. The assumption is
		# that other all numeric usernames between 4 and 20
		# digits are really pstn numbers and so they are
		# routed to the gateway
		else if (uri=~"sip:[0-9]{4,20}@.*") {
		   route(4);
		   return;
		};		
	};

	# user could not be retrieved from LDAP
	sl_send_reply("404", "User Not Found");
	return;

# end of main routing logic
}
Route block [3]

Users are not authenticated so that anyone can call INRIA's extensions


# ------------- process traffic from Internet to local PBX for SIP.edu
route[3] 
{
  # The LDAP mapping script returned a regular phone number. Let's
  # pass it to Asterisk without authentication
  rewritehostport("10.1.1.253:5060");

  append_hf("P-hint: LOCAL CALL FORWARDED TO IPBX\r\n");
  if (!t_relay()) {
    sl_reply_error();
    return;
  };
}
Route block [4]

User authentication is necessary if someone wants to call a regular PSTN phone. Exceptions are 4-digits extensions and emergency calls.


# ------------- process traffic leaving INRIA for PSTN
route[4] 
{

  # send out emergency calls to pstn gateway immediately
  if ( (uri=~"^sip:15@.*") | (uri=~"^sip:17@.*") | (uri=~"^sip:18@.*")) {
    append_hf("P-hint: EMERGENCY CALL TO GATEWAY\r\n");
    rewritehostport("10.1.1.254:5060");
    forward(uri:host, uri:port);
    return;
  };

  # four digit numeric addresses are internal freebies sent to the pbx
  # without authentication
  if (uri=~"^sip:[0-9]{4}@(10\.1\.1\.252|(softswitch\.)?inria\.fr)") {
    append_hf("P-hint: LOCAL CALL FORWARDED TO IPBX\r\n");
    rewritehostport("10.1.1.253:5060");
    forward(uri:host, uri:port);
    return;
  };

  # All numeric addresses beginning with 0 go to the pbx on the way
  # to the PSTN, authentication is triggered then
  if (uri=~"^sip:0[0-9]*@(10\.1\.1\.252|(softswitch\.)?inria\.fr)") {
    if (method=="INVITE") {
      if (!radius_proxy_authorize("inria.fr")) {
        log(1, "LOG: RADIUS authentication triggered\n");
        proxy_challenge("inria.fr","0");
	return;
      } else if (method=="INVITE" & !check_from()) {
        log(1, "LOG: Spoofed from attempt\n");
        sl_send_reply("403", "Use From=id next time");
        return;
      };
    };
  };

  rewritehostport("10.1.1.253:5060");
  append_hf("P-hint: OUTBOUND CALL FORWARDED TO IPBX\r\n");

  if (!t_relay()) {
    sl_reply_error();
    return;
  };
}

5. GNU Free Documentation License

A copy of the GNU FDL is available here : GNU Free Documentation License