Installation et utilisation de OpenSER comme un routeur SIP
2007-04-10
Switzernet Sàrl
On décrit ici l’installation et l’utilisation d’un serveur OpenSER sous Linux Debian. Après avoir suivi ce guide, on aura un serveur opérationnel (mais non configuré). OpenSER est un fork de SIP Express Router (SER). Ils présentent donc de très grandes similitudes, et leurs fichiers de configuration sont en grande partie compatibles. Comme principales différences, OpenSER est mieux documenté et intègre plus rapidement de nouvelles fonctionnalités.
OpenSER a de nombreuses fonctionnalités dans le cadre des appels VOIP. Il peut gérer les registrations, l’accounting, les échanges RADIUS, etc. Ici, nous traiterons principalement de la partie routage d’appels SIP (utilisation de SER en tant que softswitch). Voici un exemple typique d’utilisation de notre routeur SIP :

Pour pouvoir loguer les paquets SIP et utiliser le routage selon l’indicatif avec une base de données, on doit installer une base de données MySQL (ou PostgreSQL selon les préférences).
Toujours avec Synaptics ou apt-get, on installe le paquet mysql-server (c’est un méta paquet, qui installera dans notre cas mysql-server-5.0 et ses dépendances). Si le service n’est pas démarré automatiquement, la commande "/etc/init.d/mysql start" va le lancer.
Un script est fourni pour gérer la base de données MySQL utilisée par OpenSER. Ici on va simplement créer un utilisateur et une base de données openser, ainsi que les tables nécessaires. Il suffit d’exécuter le script /usr/sbin/openser_mysql avec l’argument "create". On peut garder toutes les valeurs par défaut (on appuie simplement sur entrée à chaque choix) :
switzernet8:~# /usr/sbin/openser_mysql create
MySql password for root:
Domain (realm) for the default user 'admin':
creating database openser ...
Enter password:
Install SERWEB tables ?(y/n):y
L’utilisateur openser aura des droits différents en fonction du mot de passé utilisé :
On peut se loguer directement sur MySQL pour vérifier que tout est correct. On utilise toujours le compte openserrw :
switzernet8:~# mysql -p -u openser
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 5.0.38-Debian_1-log Debian etch distribution
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| openser |
+--------------------+
Si l’utilisateur "openser" peut bien se loguer avec le mot de passe "openserrw" et que la base de données "openser" a bien été créée, tout est bien configuré.
Pour administrer le serveur à distance, il est indispensable d’utiliser un serveur SSH. On installe donc le paquet "ssh", méta paquet qui installera "openssh-server". On démarre ensuite le serveur par la commande :
/etc/init.d/ssh start
Pour toujours avoir l’heure et la date correctement réglées, on va installer un client NTP, qui permettra à notre serveur de se connecter à un serveur de temps sur internet. On installe donc le paquet "ntp". La configuration par défaut est suffisante, il nous suffit donc de lancer le service par la commande :
/etc/init.d/ntp start
Par contre, il faut ici avoir configuré le DNS correctement, sinon on ne pourra pas se connecter au serveur NTP. On peut aussi vérifier que le fuseau horaire soit correctement configuré (dans le fichier "/etc/timezone") :
openser2:~# cat /etc/timezone
Europe/Zurich
Syslog est installé par défaut sous Debian (ainsi que sur quasiment toute distribution Linux). On va ici simplement le configurer pour rediriger les logs d’OpenSER vers un fichier particulier. La configuration se fait dans "/etc/syslog.conf". On y ajoute simplement une ligne qui va indiquer dans quel fichier envoyer les logs provenant d’OpenSER. Le matching se base sur le log facility et le niveau d’alerte, sous forme "<facility>.<level>". Ici on utilise le facility "local0" comme défini dans la configuration d’OpenSER, et tous les niveaux d’alerte ".*". On choisit enfin d’envoyer les logs concernés vers le fichier "/var/log/ser.log".
…
mail.* -/var/log/mail.log
user.* -/var/log/user.log
uucp.* /var/log/uucp.log
# OpenSER
local0.* /var/log/ser.log
…
Finalement, on redémarre le service syslog :
/etc/init.d/sysklogd restart
Si le fichier ser.log n’est pas crée automatiquement on peut le créer avec la commande :
touch /var/log/ser.log
Par exemple pour une installation de Debian Etch, on aura :
# deb cdrom:[Debian GNU/Linux testing _Etch_ - Official Beta i386 CD Binary-1 20070317-21:45]/ etch contrib main
Une fois mis en commentaire.
apt-get update Met à jour la liste des paquets
apt-get install <package_name> Installe le paquet désiré
On va maintenant installer le serveur OpenSER. Sous Linux Debian (et la plupart des autres distributions), il est intégré dans le gestionnaire de paquets. On peut donc l’installer directement par exemple avec Synaptic si une interface graphique est présente sur le serveur :

Cependant, si on veut avoir la dernière version disponible d’OpenSER (version 1.2.0 au moment de l’écriture de ce guide), qui permet par exemple d’utiliser des variables dans le script de configuration, on ne peut pas utiliser directement les dépôts par défaut. De même, nous n’avons pas utilisé le dépôt officiel d’OpenSER, car celui-ci proposait encore seulement des paquets pour Debian Sarge, ce qui créait des incompatibilités de version à l’installation du module SQL.
On l’installe donc à la main, en récupérant d’abord les paquets pour Debian Etch sur le site d’OpenSER :
http://www.openser.org/pub/openser/latest/packages/deb-etch/
Le paquet de base est openser. On a aussi besoin de openser-mysql-module pour le lien avec une base de données MySQL (pour l’accounting et le module PDT), et openser-radius-modules si on veut utiliser l’authentification par RADIUS. On installe ensuite les paquets désirés avec la commande :
dpkg --install <paquet.deb>
Les fichiers de configuration d’OpenSER sont installés par défaut dans le répertoire /etc/openser. Le fichier principal est openser.cfg. La configuration se fait en écrivant un script qui décrit les actions à effectuer pour chaque paquet reçu. On a donc une très grande flexibilité pour gérer les appels SIP.
On indique l’adresse du serveur et le (les) ports sur lesquels écouter. Par défaut on écoute uniquement sur une seule interface réseau, et sur le port de signalisation SIP 5060 :
listen=<adresse IP du serveur SER>
port=5060
Si on écoute sur plusieurs interfaces (réelles ou virtuelles), il est très utile d’utiliser le paramètre "mhomed", qui permettra à OpenSER de savoir à partir de quelle interface envoyer les paquets sortants :
#Set the server to try to locate outbound interface on multihomed host.
#By default is not (0) - it is rather time consuming.
#no need of force_send_socket()
mhomed=1
listen=<adresse IP #1>
listen=<adresse IP #2>
port=5060
Pour mettre au point notre configuration de départ, il est beaucoup plus pratique de lancer openser en mode debug. Le programme va donc rester en avant-plan, et les messages vont s’imprimer sur la sortie standard (ici la console) au lieu d’être loggués par Syslog. On peut régler le niveau de debug selon la quantité d’information qu’on veut obtenir.
debug=3
fork=no
log_stderror=yes
En mode de debug, on lance openser par la commande :
"openser" ou "openser –f <fichier de configuration>".
En mode normal, le programme va lancer de multiples instances et se comporter comme un démon. On adapte donc le fichier de configuration d’Openser en conséquence :
debug=3
fork=yes
log_stderror=no
log_facility=LOG_LOCAL0
log_name="SER"
En mode service, il est recommandé d’utiliser un niveau de log de 3. Deux nouveaux paramètres nous permettent de contrôler la destination et le format des logs qui seront transmis à Syslog. Ici on utilise la facility "LOG_LOCAL0" (vérifier que cela correspond à la configuration de Syslog), et on modifie le nom qui apparaîtra dans les logs pour ne pas afficher trop de choses inutiles.
Au final, on obtiendra donc dans le fichier "/var/log/ser.log" des informations sous la forme :
May 23 12:02:54 localhost SER[9359]: [+4121693XXXX to +4121550XXXX] ACK +4121550XXXX@195.XXX.XXX.XXX, 212.XXX.XXX.XXX to 195.XXX.XXX.XXX (390-0)
May 23 12:02:58 localhost SER[9360]: [+4121693XXXX to +4121550XXXX] ACK +4121550XXXX@195.XXX.XXX.XXX, 212.XXX.XXX.XXX to 195.XXX.XXX.XXX (390-0)
Evidemment tout dépendra de ce qu’on décide de loguer dans le fichier de configuration d’OpenSER.
La configuration générale du service se trouve dans le fichier "/etc/default/openser". On a juste à changer la valeur de la variable "RUN_OPENSER", de "no" à "yes" :
#
# OpenSER startup options
#
# Set to yes to enable openser, once configured properly.
RUN_OPENSER=yes
# User to run as
USER=openser
# Group to run as
GROUP=openser
# Amount of memory to allocate for the running OpenSER server (in Mb)
MEMORY=64
# Enable the server to leave a core file when it crashes.
# Set this to 'yes' to enable OpenSER to leave a core file when it crashes
# or 'no' to disable this feature. This option is case sensitive and only
# accepts 'yes' and 'no' and only in lowercase letters.
DUMP_CORE=no
Le service OpenSER se lancera automatiquement au démarrage de l’ordinateur. On peut aussi démarrer ou stopper OpenSER avec le script d’init fourni :
/etc/init.d/openser start/stop/restart
Dans OpenSER, les modules sont des librairies chargées à la demande, qui fournissent des fonctionnalités supplémentaires (de nouvelles fonctions ou variables). Les principaux modules utilisés par défaut sont ceux qui gèrent les transactions, en stateful ou stateless (tm.so et sl.so) ainsi que le modules fournissant les fonctions de routage (rr.so). La documentation des différents modules est disponible à l’adresse (à adapter à la version utilisée) :
http://www.openser.org/docs/modules/1.2.x/
En plus des modules de base chargés par la configuration par défaut, on va utiliser certains modules supplémentaires. On indique les modules à utiliser dans le fichier de configuration, avec la commande loadmodule "<nom du module>.so".
Pour éviter d’entrer le chemin complet de chaque module, on peut spécifier le répertoire ou sont situés les modules au début du fichier de configuration d’OpenSER :
mpath="/usr/lib/openser/modules"
Ce module prend en charge toute la partie transaction "stateful" lors des échanges SIP. C’est un module indispensable et chargé par défaut. Comme paramètres utiles, on peut modifier les délais de timeout. Par exemple pour diminuer à 10 secondes le délai avant d’annuler l’appel au cas ou un hôte ne répond pas, on peut utiliser :
modparam("tm", "fr_timer", 10)
Ce module permet de communiquer avec une base de données MySQL. Il sera utilisé pour l’accounting et le module PDT.
Ce module fournit une méthode automatisée pour gérer le load balancing entre différents gateways SIP.
Ce module effectue l’accounting (logue les paquets reçus). On peut l’utiliser soit avec une base de données, soit en envoyant les informations à Syslog. Pour loguer les paramètres dans la base de données, on entre les paramètres suivants :
modparam("acc", "db_url", "mysql://openser:openserrw@localhost/openser")
modparam("acc", "db_flag", 1) # flag pour les "appels réussis"
modparam("acc", "db_missed_flag", 2) # flag pour les "appels manqués"
Par défaut, les paquets avec un statut d’erreur seront inscrits dans la table missed_calls, et les paquets avec un statut normal dans la table acc. Toutefois, il faut marquer nous-même le statut du paquet dans le script de configuration, en utilisant dans notre cas les commandes :
setflag(1); ou setflag(2);
La table MySQL ou seront stockées les informations d’accounting contient les champs suivants :
+------------+--------------+------+-----+---------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------------------+-------+
| sip_from | varchar(128) | NO | | | |
| sip_to | varchar(128) | NO | | | |
| sip_status | varchar(128) | NO | | | |
| sip_method | varchar(16) | NO | | | |
| i_uri | varchar(128) | NO | | | |
| o_uri | varchar(128) | NO | | | |
| from_uri | varchar(128) | NO | | | |
| to_uri | varchar(128) | NO | | | |
| sip_callid | varchar(128) | NO | MUL | | |
| username | varchar(64) | NO | MUL | | |
| domain | varchar(128) | NO | | | |
| fromtag | varchar(128) | NO | | | |
| totag | varchar(128) | NO | | | |
| time | datetime | NO | | 0000-00-00 00:00:00 | |
| timestamp | timestamp | NO | | CURRENT_TIMESTAMP | |
| src_leg | varchar(128) | YES | | NULL | |
| dst_leg | varchar(128) | YES | | NULL | |
+------------+--------------+------+-----+---------------------+-------+
On remarque ici que SER ne gère pas l’appel complet, mais a uniquement la notion des transactions SIP. Par exemple on n’aura pas ici la durée d’appel, mais une entrée pour chaque transaction (avec les messages INVITE, ACK, CANCEL et BYE logués séparément).
Pour utiliser le module acc avec une sortie vers Syslog, on utilise le paramètre suivant :
modparam("acc", "log_level", 1)
Ce module permet d’utiliser une table SQL avec des règles de routage selon le pattern du numéro appelé et l’adresse IP du serveur source (sdomain). On l’utilise pour changer l’adresse IP du serveur de destination (domain). Ces règles de routage sont stockées dans la table pd_multidomain de la base de donnée pdt.
La base de données pdt n’est pas crée par le script fourni par OpenSER, mais un script spécifique au module est disponible. Comme il est utile de le modifier pour ajouter un attribut "description" à chaque couple sdomain/prefix, on utilise un script (pdt.sql) légèrement modifié est disponible :
DROP DATABASE IF EXISTS pdt;
CREATE DATABASE pdt;
USE pdt;
-- create table
CREATE TABLE pd_multidomain (
sdomain VARCHAR(255) NOT NULL,
prefix VARCHAR(32) NOT NULL,
domain VARCHAR(255) NOT NULL DEFAULT "",
description VARCHAR(255) NOT NULL DEFAULT "",
PRIMARY KEY (sdomain, prefix)
);
-- give permissions tu openser user
GRANT ALL ON pdt.pd_multidomain TO openser;
Après l’avoir exécuté (avec les droits de l’utilisateur MySQL root), on a donc une nouvelle base de données pdt, qui contient la table pd_multidomain :
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| sdomain | varchar(255) | NO | PRI | | |
| prefix | varchar(32) | NO | PRI | | |
| domain | varchar(255) | NO | | | |
| description | varchar(255) | NO | | | |
+-------------+--------------+------+-----+---------+-------+
On peut par exemple y insérer une nouvelle ligne avec la commande suivante :
INSERT INTO pd_multidomain VALUES("128.179.67.78", "41", "128.179.67.35", "SIP1");
+----------------+---------+----------------+-------------+
| sdomain | prefix | domain | description |
+----------------+---------+----------------+-------------+
| 128.179.67.76 | 41 | 212.XXX.XX.XXX | OUT1 |
| 128.179.67.78 | 4121550 | 128.179.67.35 | SIP1 |
+----------------+---------+----------------+-------------+
1) il faut absolument mettre une destination (est-ce qu’on peut mettre ‘%’ pour tous les destinations ?) et domain source
Ce module simplifie l’affichage de messages, où on peut utiliser des variables et des couleurs.
De nombreuses variables peuvent aussi être affichées, en utilisant la syntaxe $<nom de variable>. La liste des variables utilisables est disponible dans la documentation :
http://openser.org/dokuwiki/doku.php/pseudovariables:1.2.x
Pour du routage simple, on utilise principalement les variables suivantes :
$fd "From domain", le domaine source du paquet
$tu "To URI", URI SIP de destination du paquet (reste fixe)
$ruri URI SIP de destination du paquet (est mise à jour après des opérations telles que "rewritehost")
Pour les couleurs on utilise des balises notées $Cab, ou le "a" représente la couleur du premier plan, et le "b" la couleur de l’arrière-plan. De plus, la lettre "x" représente la couleur par défaut du terminal.
Par exemple pour écrire une ligne en bleu sur fond blanc (sur un terminal blanc à l’origine), on utilise :
xlog("$CbxLigne de texte.$Cxx\n);
Le $Cxx final remet les couleurs par défaut du terminal.
Si l’un des serveurs SIP de destination filtre les adresses IP de destinations des paquets SIP, ou en cas de configuration particulière (par exemple utilisation d’un tunnel IPSec), il peut être utile d’avoir plusieurs interfaces réseau sur le serveur OpenSER. Ici nous décrivons l’utilisation d’OpenSER avec une interface réseau réelle et une autre virtuelle. Dans ce cas particulier, l’adresse IP de l’interface virtuelle n’est pas routable.
Pour la configuration d’OpenSER, rien ne distingue les interfaces réelles et virtuelles. Il suffit de mettre deux paramètres "listen" dans la configuration d’OpenSER. Si l’on a défini "mhomed=1" dans le fichier de configuration, le serveur devrait découvrir par quelle interface envoyer chaque paquet. Sans ce paramètre, on peut spécifier l’interface à utiliser en sortie avec la commande "force_send_socket(<adresse IP>)".
Sous Linux Debian, on déclare une interface virtuelle dans le même fichier que les interfaces réelles, "/etc/network/interfaces". Sur notre serveur, la configuration est la suivante :
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
allow-hotplug eth0
iface eth0 inet static
address 212.XXX.XXX.XXX
netmask 255.255.255.240
broadcast 212.XXX.XXX.XXX
gateway 212.XXX.XXX.XXX
auto eth0
iface eth0:0 inet static
address 195.XXX.XXX.XXX
netmask 255.255.255.255
post-up /etc/network/if-post-up.d/add_routes_SIP.sh
auto eth0:0
eth0 est l’interface réelle. Elle est configurée traditionnellement. C’est donc là qu’on définit le gateway par défaut.
Sur cette interface on définit notre interface virtuelle, eth0:0. On utilise un masque de sous réseau de 255.255.255.255, comme elle ne fait pas partie d’un réseau (et n’est même pas routable).
Après redémarrage du réseau, cette configuration crée une route par défaut, qui renvoie les paquets à destination du sous réseau réel vers le gateway.
openser2:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
212.XXX.XX.X 0.0.0.0 255.255.255.240 U 0 0 0 eth0
0.0.0.0 212.XXX.XX.X 0.0.0.0 UG 0 0 0 eth0
On peut aussi vérifier que tout soit correct et que les interfaces sont bien montées ("up") avec la commande "ifconfig" :
openser2:~# ifconfig
eth0 Link encap:Ethernet HWaddr 00:18:F3:FC:D3:CA
inet addr:212.XXX.XX.X Bcast:212.XXX.XX.XX Mask:255.255.255.240
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:759138 errors:0 dropped:0 overruns:0 frame:0
TX packets:877477 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:297606244 (283.8 MiB) TX bytes:626141253 (597.1 MiB)
Interrupt:177 Base address:0xe000
eth0:0 Link encap:Ethernet HWaddr 00:18:F3:FC:D3:CA
inet addr:195.XXX.XXX.XX Bcast:195.XXX.XXX.XXX Mask:255.255.255.255
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Interrupt:177 Base address:0xe000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:134109 errors:0 dropped:0 overruns:0 frame:0
TX packets:134109 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:257876593 (245.9 MiB) TX bytes:257876593 (245.9 MiB)
On va ensuite ajouter des règles de routage spécifiques, qui vont envoyer les paquets à destination de certaines adresses vers une destination particulière, et faire qu’ils proviennent de l’interface virtuelle eth0:0. Ici, on utilise ce système pour envoyer certains paquets SIP vers le point d’accès d’entrée d’un tunnel IPSec. En sortie du tunnel, les paquets seront donc vus comme provenant de l’interface virtuelle 195.XXX.XXX.XX.
On ajoute les règles suivantes, définies dans le fichier "/etc/network/if-post-up.d/add_routes_SIP.sh". Ce fichier est exécuté automatiquement après que l’interface eth0 soit montée. Ne pas oublier de le rendre exécutable avec la commande "chmod +x <fichier.sh>". Les route sont définies comme suit :
#!/bin/bash
route add -host 212.XXX.XX.XXX dev eth0:0 gw 212.XXX.XX.XX
route add -host 62.XXX.XX.X dev eth0:0 gw 212.XXX.XX.XX
Nous avons maintenant deux nouvelles règles dans notre table de routage (on remarque que la commande route indique eth0 et non eth0:0 pour l’interface utilisée, mais le routage spécifié est malgré tout correctement pris en compte) :
openser2:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
62.XXX.XX.X 212.XXX.XX.XX 255.255.255.255 UGH 0 0 0 eth0
212.XXX.XX.XXX 212.XXX.XX.XX 255.255.255.255 UGH 0 0 0 eth0
212.XXX.XX.X 0.0.0.0 255.255.255.240 U 0 0 0 eth0
0.0.0.0 212.XXX.XX.X 0.0.0.0 UG 0 0 0 eth0