Asterisk en routeur d'appels : Par Toutatis, ça capte mal dans ce tunnel !

GNU/Linux Magazine n° 170 | avril 2014 | Emile (iMil) Heitor
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
« Bonjour, êtes-vous équipé en fenêtclic-Bienvenue chez iMil, si vous êtes un démarcheur, restez en ligne, un opérateur va vous répondre... ». Avouez, avouez que vous n'en pouvez plus de recevoir des coups de fil anonymes de vendeurs de fenêtres et autres démarcheurs téléphoniques. Eh bien pour ça aussi, UNIX a une solution, elle s'appelle Asterisk.

1. La VoIP, c'est pas si compliqué

Bon ok, c'est pas si compliqué, mais ça peut vite devenir agaçant. J'y reviendrai plus tard dans l'article. N'en reste pas moins qu'il existe, sur nos UNIX modernes, un logiciel qui, de plus en plus, dame le pion à nombre de solutions de Voix sur IP propriétaires, je veux bien entendu parler d'Asterisk.

Asterisk est essentiellement connu pour sa fonctionnalité d'IPBX, ou commutateur téléphonique privé utilisant le protocole IP, mais le logiciel est capable de bien plus ; en particulier, Asterisk peut se comporter comme un routeur d'appels, un softswitch, n'en déplaise aux vendeurs de tels serveurs propriétaires dont le dédain envers les solutions libres me rappelle des souvenirs... [/rant]

Dans cet article, nous mettrons l'accent sur la fonctionnalité phare, nous utiliserons Asterisk comme l'IPBX du foyer et nous évertuerons à faire de la vie des démarcheurs téléphoniques un véritable cauchemar.

2. Mais la théorie, c'est... pffffff

Je vous l'accorde. Mais dans le monde tortueux de la voix, il est nécessaire de disposer d'un minimum de notions, car on ne communique pas avec la voix comme on affiche une page web. Malheureusement.

LE protocole inévitable lorsqu'on évoque la VoIP, c'est SIP. Abréviation de Session Initiation Protocol, SIP, normalisé par l'IETF, a pris le pas sur le protocole H323 issu du monde des télécoms. Il est important de comprendre que SIP, seul, ne transporte pas les données en soi ; aucun morceau de voix n'est véhiculé par ce protocole, cette tâche est reléguée au protocole RTP, et nous verrons que les pires casse-tête émanent de ce fonctionnement bicéphale. Ainsi, lorsqu'on compose un numéro de téléphone avec un terminal SIP, une foule d'informations sont échangées, et parmi elles, l'adresse IP des serveurs de média, dans le meilleur des cas les terminaux eux-mêmes. S'établit alors une connexion entre les terminaux ; c'est via cette dernière que vous pouvez écouter les dernières péripéties de votre grand-mère autour du monde.

Rentrons un peu plus loin dans le vif du sujet :

2.1 L'enregistrement

Un terminal SIP, pour être reconnu par le logiciel de commutation, doit « s'enregistrer », comprendre que ce dernier dispose le plus souvent d'un identifiant, classiquement un couple login / password. L'authentification s'effectue par le biais du protocole SIP vers un SIP Registrar, l'équipement en charge de tenir la base de données (quelle que soit sa forme) des terminaux. Voici à quoi ressemble un échange d'enregistrement :

- Le terminal fait une demande d'enregistrement : REGISTER ;

- Le SIP Registrar renvoie un code 401 (Unauthorized) et annonce la méthode d'authentification, ainsi qu'une chaîne de caractères qui permettra de créer un hash et sécuriser l'échange ;

- Le terminal, une fois ces éléments connus, va renvoyer une requête de type REGISTER avec les informations nécessaires à son enregistrement (utilisateur, IP, mot de passe hashé, algorithme...) ;

- Le registrar renvoie alors une réponse 200 OK, le terminal est désormais connu.

Voici une trace SIP classique qui illustre ce comportement :

- 192.168.1.10 est le terminal, ici un téléphone SIP de marque SNOM ;

- 192.168.2.1 est le SIP registrar.

2.1.1 Le terminal envoie la demande d'enregistrement

REGISTER sip:192.168.2.1 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-gmdpiz3f19hu;rport
From: "iMil" <sip:snom300@192.168.2.1>;tag=g52o3lkawt
To: "iMil" <sip:snom300@192.168.2.1>
Call-ID: 3c4210ba5a55-09spjcv2gxwh@snom300-000413254E2B
CSeq: 2394 REGISTER
Max-Forwards: 70
Contact: <sip:snom300@192.168.1.10:2051;line=1gvc26v8>;flowid=
1;q=1.0;+sip.instance="<urn:uuid:c049091f-8a4a-433c-9253-1c5aba144
02d>";audio;mobility="fixed";duplex="full";description="snom300";acto
r="principal";events="dialog";methods="INVITE,ACK,CANCEL,BYE,REFER,OP
TIONS,NOTIFY,SUBSCRIBE,PRACK,MESSAGE,INFO"
User-Agent: snom300/6.0.3
Supported: gruu
Allow-Events: dialog
X-Real-IP: 192.168.1.10
WWW-Contact: <http://192.168.1.10:80>
WWW-Contact: <https://192.168.1.10:443>
Expires: 0
Content-Length: 0

2.1.2 Le registrar refuse l'enregistrement et donne les informations nécessaires dans l'entête WWW-Authenticate

SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-gmdpiz3f19hu;received=192.168.1
.10;rport=2051
From: "iMil" <sip:snom300@192.168.2.1>;tag=g52o3lkawt
To: "iMil" <sip:snom300@192.168.2.1>;tag=as32c7322b
Call-ID: 3c4210ba5a55-09spjcv2gxwh@snom300-000413254E2B
CSeq: 2394 REGISTER
Server: Asterisk PBX 10.12.4
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
WWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="59c26ba4"
Content-Length: 0

2.1.3 Le terminal utilise l'algorithme et le hash fournis afin de renouveler la demande

REGISTER sip:192.168.2.1 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-1uu4fahipil5;rport
From: "iMil" <sip:snom300@192.168.2.1>;tag=g52o3lkawt
To: "iMil" <sip:snom300@192.168.2.1>
Call-ID: 3c4210ba5a55-09spjcv2gxwh@snom300-000413254E2B
CSeq: 2395 REGISTER

Max-Forwards: 70
Contact: <sip:snom300@192.168.1.10:2051;line=1gvc26v8>;flowid=
1;q=1.0;+sip.instance="<urn:uuid:c049091f-8a4a-433c-9253-1c5aba144
02d>";audio;mobility="fixed";duplex="full";description="snom300";acto
r="principal";events="dialog";methods="INVITE,ACK,CANCEL,BYE,REFER,OP
TIONS,NOTIFY,SUBSCRIBE,PRACK,MESSAGE,INFO"
User-Agent: snom300/6.0.3
Supported: gruu
Allow-Events: dialog
X-Real-IP: 192.168.1.10
WWW-Contact: <http://192.168.1.10:80>
WWW-Contact: <https://192.168.1.10:443>
Authorization: Digest username="snom300",realm="asterisk",nonce="59c2
6ba4",uri="sip:192.168.2.1",response="deadbeeff00b42",algorithm=md5
Expires: 0
Content-Length: 0

2.1.4 Le registrar accepte l'enregistrement

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-o0jvzcoumuk8;received=192.168.1.10;rpo
rt=2051
From: "iMil" <sip:snom300@192.168.2.1>;tag=s91dzemvls
To: "iMil" <sip:snom300@192.168.2.1>;tag=as5a6ef160
Call-ID: 3c421e6f5f37-y33h2d0xes2s@snom300-000413254E2B
CSeq: 2396 REGISTER
Server: Asterisk PBX 10.12.4
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Expires: 3600
Contact: <sip:snom300@192.168.1.10:2051;line=1gvc26v8>;expires=3600
Date: Sun, 16 Feb 2014 18:23:08 GMT
Content-Length: 0

Dans le monde de la VoIP, on représente classiquement ces échanges à l'aide d'un Call Flow. Considérant la verbosité d'une trace SIP, l'analyse et l'éventuel débogage d'un dysfonctionnement sont rendus plus accessibles par la visualisation d'un tel graphique. En outre, le fameux logiciel d'analyse réseau Wireshark, permet de générer de tels graphes à l'aide du menu Telephony. Le rendu étant quelque peu étriqué, nous y préférerons, pour cet article, un graphe un peu plus explicite (Fig. 1).

Fig. 1

2.2 L'appel

Une fois nos terminaux enregistrés, la seconde étape délicate consiste en l'appel lui-même. En simplifiant, il se déroule ainsi :

- Une requête INVITE est émise par le terminal. Elle est munie d'une SDP, ou Session Description Protocol, dans laquelle le terminal annonce ses capacités en termes de codecs, ainsi que l'adresse IP sur laquelle auront lieu les communications média (ici, la voix) ;

- Le serveur SIP répond immédiatement avec l'entête Trying et transmet l'invitation du terminal appelant au terminal appelé. Du fait de ce mode de fonctionnement, on nomme le serveur SIP Proxy ;

- Le terminal appelé (et évidemment enregistré) renvoie une réponse Ringing au SIP Proxy, qui relaye ce message au terminal appelant.

La trace de cet échange est plus complexe :

INVITE sip:1022@192.168.2.1;user=phone SIP/2.0
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-kal7l3klt5hg;rport
From: "iMil" <sip:snom300@192.168.2.1>;tag=8gad22euu0
To: <sip:1022@192.168.2.1;user=phone>
Call-ID: 3c424be8975e-plr7wrnrxtzq@snom300-000413254E2B
CSeq: 2 INVITE
Max-Forwards: 70
Contact: <sip:snom300@192.168.1.10:2051;line=1gvc26v8>;flow-id=1
P-Key-Flags: keys="3"
User-Agent: snom300/6.0.3
Accept: application/sdp
Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, SUBSCRIBE, PRACK,
MESSAGE, INFO
Allow-Events: talk, hold, refer
Supported: timer, 100rel, replaces, callerid
Session-Expires: 3600;refresher=uas
Authorization: Digest username="snom300",realm="asterisk",nonce="deadbeef",uri=
"sip:1022@192.168.2.1;user=phone",response="5949cbdf9e041889612f9da3d03e50f9",a
lgorithm=md5
Content-Type: application/sdp
Content-Length: 370
v=0
o=root 1779652087 1779652087 IN IP4 192.168.1.10
s=call
c=IN IP4 192.168.1.10
t=0 0
m=audio 50056 RTP/AVP 0 8 9 2 3 18 4 101
a=rtpmap:0 pcmu/8000
a=rtpmap:8 pcma/8000
a=rtpmap:9 g722/8000
a=rtpmap:2 g726-32/8000
a=rtpmap:3 gsm/8000
a=rtpmap:18 g729/8000
a=rtpmap:4 g723/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=ptime:20
a=sendrecv

L'INVITE est le message initial émis par le terminal. L'entête Content-Type indique que la SDP est contenue dans la suite de ce message, cette dernière contient toutes les informations nécessaires à l'établissement de la session média, en particulier :

- o permet d'identifier l'initiateur de l'appel,

- c présente les informations de connexion,

- a tous les attributs média, par exemple les codecs que le terminal supporte.

SIP/2.0 100 Trying
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-kal7l3klt5hg;received=192.168
.1.10;rport=2051
From: "iMil" <sip:snom300@192.168.2.1>;tag=8gad22euu0
To: <sip:1022@192.168.2.1;user=phone>
Call-ID: 3c424be8975e-plr7wrnrxtzq@snom300-000413254E2B
CSeq: 2 INVITE
Server: Asterisk PBX 10.12.4
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO,
PUBLISH
Supported: replaces, timer
Session-Expires: 1800;refresher=uas
Contact: <sip:1022@192.168.2.1:5060>
Content-Length: 0

Le message TRYING est immédiatement renvoyé par le SIP Proxy qui informe le terminal que le processus est en cours. Dans le même temps, le SIP Proxy relaye le message d'invite vers le terminal appelé, ce dernier contient les informations qui ont été transmises par l'appelant possiblement modifiées par le proxy, comme nous le verrons plus tard. Le terminal appelé informe le proxy qu'il est en train de sonner (message 180 Ringing), le message est alors transmis au terminal appelant qui entendra alors le fameux Ringback Tone, ou tonalité de retour d'appel :

SIP/2.0 180 Ringing
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-kal7l3klt5hg;received=192.168.1
.10;rport=2051
From: "iMil" <sip:snom300@192.168.2.1>;tag=8gad22euu0
To: <sip:1022@192.168.2.1;user=phone>;tag=as60eb8c3c
Call-ID: 3c424be8975e-plr7wrnrxtzq@snom300-000413254E2B
CSeq: 2 INVITE
Server: Asterisk PBX 10.12.4
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Session-Expires: 1800;refresher=uas
Contact: <sip:1022@192.168.2.1:5060>
Content-Length: 0

Lorsque l'utilisateur décroche le terminal appelé, un message 200 OK est envoyé au SIP proxy, qui à son tour, le transmet au terminal appelé. Dans cette réponse, on trouvera également une SDP qui indique au terminal appelant les coordonnées et capacités de l'appelant. Enfin, l'appelant acquittera cette réponse à l'aide d'un message ACK. L'établissement de la session média, et donc de la conversation, peut alors avoir lieu :

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-kal7l3klt5hg;received=192.168.1
.10;rport=2051
From: "iMil" <sip:snom300@192.168.2.1>;tag=8gad22euu0
To: <sip:1022@192.168.2.1;user=phone>;tag=as60eb8c3c
Call-ID: 3c424be8975e-plr7wrnrxtzq@snom300-000413254E2B
CSeq: 2 INVITE
Server: Asterisk PBX 10.12.4
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH

Supported: replaces, timer
Session-Expires: 1800;refresher=uas
Contact: <sip:1022@192.168.2.1:5060>
Content-Type: application/sdp
Require: timer
Content-Length: 314
v=0
o=root 485727824 485727824 IN IP4 192.168.1.11
s=Asterisk PBX 10.12.4
c=IN IP4 192.168.1.11
t=0 0
m=audio 19010 RTP/AVP 3 0 8 101
a=rtpmap:3 GSM/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=silenceSupp:off - - - -
a=ptime:20
a=sendrecv
ACK sip:1022@192.168.2.1:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.10:2051;branch=z9hG4bK-cu4e80h6zf8q;rport
From: "iMil" <sip:snom300@192.168.2.1>;tag=8gad22euu0
To: <sip:1022@192.168.2.1;user=phone>;tag=as60eb8c3c
Call-ID: 3c424be8975e-plr7wrnrxtzq@snom300-000413254E2B
CSeq: 2 ACK
Max-Forwards: 70
Contact: <sip:snom300@192.168.1.10:2051;line=1gvc26v8>;flow-id=1
Content-Length: 0

La session RTP s'établira sur un codec commun, et dans le cas présent, la communication média s'effectuera directement d'un terminal à l'autre, sans équipement tiers. On représente cette mise en relation avec le très classique schéma représenté en figure 2.

Fig. 2

3. NAT et VoIP == Roquefort et confiture

« Dans le cas présent, la communication média s'effectuera directement d'un terminal à l'autre ». Ah-ah, contemplez avec quelle légèreté je balaye d'un revers de la main une quelconque problématique liée à l'établissement de la session média. Quelle insouciance, quelle naïveté ! Non. La vérité, c'est que l'établissement des médias, dans un réseau domestique, c'est un véritable cauchemar, un instrument de torture du moyen âge, chute de cheveux et assèchement de la peau. Les médias et le NAT, c'est l'ENFER.

Ça y est ? Je vous ai bien motivé là ? On y va.

Les traces SIP précédentes, et en particulier les informations contenues dans les SDP, contiennent les adresses IP des terminaux. Lorsque vous passez nonchalamment un coup de téléphone à travers votre [opérateur]box, l'adresse inscrite dans la SDP est l'IP publique qui vous est affectée par le fournisseur d'accès ; aucun souci en perspective dans ce cas de figure, les équipements communiquent en direct. Mais que se produira-t-il si votre terminal SIP dispose d'une IP de type RFC1918, non routable (dite « privée ») ? Eh bien c'est simple : vous n'entendrez rien. Le terminal appelé sonnera, vous entendrez le ringback tone, le « décroché » enverra les bonnes séquences, mais rien. Pas un son. Le néant. Pourquoi ? Parce que le terminal appelé essaiera en vain d'établir une connexion RTP vers une adresse qui n'est pas accessible depuis son réseau. Et c'est là que les ennuis commencent. En effet, comme nous l'avons vu précédemment, le protocole SIP ne transporte que la signalisation, la voix - le média - transite par le biais d'un autre protocole, RTP, qui pour simplifier la tâche, utilisera un port au hasard. Oui parce que sinon c'est trop simple.

Il existe une multitude de techniques, plus ou moins efficaces, pour contourner ce casse-tête. La plus simple consiste à utiliser du tunneling IP de bout en bout ; cette solution est convenable dans le cadre d'une téléphonie privée, mais n'adresse pas la téléphonie publique.

Une autre consiste en l'utilisation d'un proxy RTP sur l'équipement public faisant office de passerelle sur votre réseau ; c'est par exemple cette technique que j'ai utilisé pour bypasser la RougeBox fournie par mon opérateur. Dans le cadre de notre article, j'ai opté pour une solution assez commune à base d'iptables et de directives appropriées d'Asterisk (Fig. 3). Concrètement, nous allons faire arriver le port 5060 (le port SIP standard) de la passerelle sur une machine virtuelle numérotée sur un adressage « privé ». C'est sur cette machine virtuelle que tourne notre IPBX.

De la même façon, Asterisk définit une plage de ports dédiés au RTP, nous allons rediriger ces ports depuis la passerelle vers notre VM.

Fig. 3

Ces règles se placent dans la chaîne PREROUTING et sont relativement simples :

- Redirection du protocole SIP :

# iptables -t nat -A PREROUTING -i eth0
-p udp -m udp --dport 5060 -j DNAT --todestination
192.168.2.1

- Redirection du flux RTP, que le fichier rtp.conf d'Asterisk définit comme transitant sur les ports 10000 à 20000 :

# iptables -t nat -A PREROUTING -i eth0 -p
udp -m udp --dport 10000:20000 -j DNAT --todestination
192.168.2.1

C'est tout ? Eh-eh-eh. Non. Quand bien même le flux RTP est-il transporté jusqu'à notre réseau domestique, la problématique de l'adresse média présente dans la SDP reste entière. Comment faire ? Modifier la SDP. Point de techniques barbares, Asterisk gère cela nativement à l'aide d'un paramètre bien nommé : media_address. Cette variable, que nous allons placer dans le fichier de configuration sip.conf, permettra à l'IPBX de remplacer l'adresse média présente dans la SDP par notre adresse publique.

4. Et sinon on fait des trucs un peu ?

Oui, oui, on arrête - presque - là la théorie pour manipuler du fichier de configuration et passer à la pratique, mais comme je vous l'exposais au début de cet article, on n'aborde pas la téléphonie sur IP sans en comprendre les fondements, car si au détour d'un forum quelconque on trouvera probablement des paramètres adaptés, il est toutefois plus agréable de comprendre les objets que l'on manipule.

Asterisk existe sous forme de paquet pour l'intégralité des systèmes UNIX (libres) modernes. Mon IPBX fonctionne sous NetBSD, mais il va sans dire que FreeBSD, OpenBSD, Debian et dérivés, ainsi que Red Hat et dérivés disposent tous d'un paquet (plus ou moins) à jour du logiciel. Ainsi, choisissez la commande qui vous sied parmi pkgin, pkg, apt-get, aptitude, yum et installez le paquet asterisk. De mon côté, il s'agit de la version 10.12.4.

Nous nous concentrerons sur deux fichiers de configuration :

- sip.conf, qui contient les informations relatives aux inscriptions SIP entrantes et sortantes ;

- extensions.conf décrit ce qu'on appelle en VoIP un dialplan : il s'agit des règles de numérotation et de leurs traitements. Nous reviendrons en détails sur la puissance que cachent ces règles.

La première étape de la mise en place de notre système de téléphonie consiste en l'enregistrement de nos divers terminaux. J'utiliserai pour la démonstration un téléphone SIP de marque SNOM et un classique téléphone mobile Android, capable depuis quelques temps de s'enregistrer sur un SIP registrar nativement, sans passer par un logiciel tiers. Pour ceux d'entre vous ne disposant pas d'une version d'Android supportant ce fonctionnement, il est également possible de profiter de la voix sur IP à l'aide du logiciel CSipSimple, disponible sur Google Play mais également sous forme de paquet individuel sur le site du projet. CSipSimple est sous licence GNU GPL v3.

À noter que les téléphones IP SNOM fonctionnent sous GNU/Linux et que depuis leur interface web, il est possible d'obtenir des traces SIP, très pratiques pour les séances de débogage.

Le fichier sip.conf se présente sous la forme connue de configuration suivante :

[section1]
valeur = clé
[section2]
valeur = clé

Ici, les sections représentent les interlocuteurs SIP à l'exception de la section [general] qui s'applique à l'ensemble des sections. Voici un fichier sip.conf minimal :

[general]
nat=no
[poste1]
type=friend
context=desk
username=jeanmi
secret=skinautique
host=dynamic
[poste2]
type=friend
context=desk
username=micheline
secret=peinturesurbois
host=dynamic

Comme on le comprend aisément, notre configuration comprend deux utilisateurs, dont le login est spécifié par le champ username et le mot de passe par le champ secret. La directive nat=no s'applique à l'ensemble des postes, mais l'on pourra écraser cette valeur dans chaque section si nécessaire en donnant ponctuellement une valeur différente à nat=. La directive type définit la catégorie de l'interlocuteur SIP, elle peut prendre trois valeurs :

- peer, il s'agit d'une entité SIP à laquelle Asterisk envoie des appels. Typiquement, votre opérateur SIP ;

- user, c'est une entité qui place des appels à l'aide d'Asterisk, par exemple un terminal qui ne peut qu'émettre des appels ;

- friend, cette entité est un peer et un user, comprendre qu'elle peut recevoir des appels d'Asterisk ou en émettre.

Le champ host est ici défini comme dynamique simplement parce que le terminal s'enregistre seul, inutile donc d'en préciser l'IP, il se présentera lors de son enregistrement. Enfin, le champ context définit le contexte auquel est associé le terminal ; cette variable prendra tout son sens lorsque nous manipulerons le fichier extensions.conf dans lequel nous définirons un dialplan.

Munis de ces informations, nous pouvons d'ores et déjà démarrer l'IPBX ou encore recharger sa configuration SIP. Asterisk dispose d'une Command Line Interface similaire à celle d'un routeur. Cette dernière peut être accédée à l'aide des commandes asterisk -r ou encore rasterisk. Pour recharger uniquement la configuration SIP, on utilise la commande CLI :

asterisk*CLI> sip reload

À des fins d'observation, il s'avère souvent judicieux d'activer le mode debug de la pile SIP d'Asterisk, ceci s'effectue à l'aide de la commande CLI :

asterisk*CLI> sip set debug on
SIP Debugging enabled

Dès lors que cette configuration sera rechargée, les terminaux de jeanmi et micheline pourront s'enregistrer sur le PBX et s'appeler. Les informations à renseigner sur le client SIP sont généralement :

- Nom d'utilisateur,

- Mot de passe,

- Serveur (implicitement Sip registrar).

Pour exemple, voir en figure 4 la configuration de mon téléphone SNOM.

Fig. 4

La configuration SIP sur les mobiles Android est cachée dans les paramètres de téléphonie.

- Démarrez l'application « téléphone »,

- Entrez dans les préférences de l'application (trois points en bas à droite sur une Kit Kat officielle),

- Choisissez Call Settings,

- En bas de la liste déroulante, choisissez SIP Accounts.

Une fois les configurations appliquées sur vos terminaux, ces derniers devraient être enregistrés sur votre IPBX. Pour vous en convaincre, utilisez la commande Asterisk suivante :

korriban*CLI> sip show peers
Name/username Host Dyn Forcerport ACL Port Status Description
poste1/jeanmi 192.168.1.10 D 49731 Unmonitored
poste2/micheline 192.168.1.11 D 46327 Unmonitored

Une dernière opération est nécessaire pour permettre à nos deux utilisateurs de communiquer : leur attribuer un numéro de téléphone. Pour ce faire, nous allons découvrir le fichier extensions.conf.

Ce fichier contient toute l'intelligence du routage d'appels. C'est ici, par le biais des contextes d'appel et des extensions déclarées, que nous pouvons diriger un appel entrant vers la bonne destination, qu'il s'agisse d'un terminal, d'un opérateur SIP externe ou encore... d'un script :)

Pour le moment, nous nous contenterons de déclarer un contexte simple dans lequel nous associerons un numéro de ligne interne à nos terminaux précédemment déclarés dans le fichier sip.conf. extensions.conf est structuré de la même façon que sip.conf, il en est de même pour l'ensemble des fichiers de configuration d'Asterisk :

[desk]
exten => 10,1,Dial(SIP/poste1,5)
exten => 11,1,Dial(SIP/poste2,5)

Ici, on déclare le contexte desk et deux extensions : 10 et 11. Le numéro de poste est suivi d'un numéro de séquence qu'on incrémente si d'autres opérations sont nécessaires. Par exemple, si l'on souhaite simplement diffuser un message lorsque la ligne 12 est appelée, on aura la séquence suivante :

exten => 12,1,Answer()
exten => 12,2,Playback(mon_message)
exten => 12,3,Hangup()

Notez que pour des raisons de simplicité, il est souvent préférable d'utiliser le caractère n après la séquence 1, qui implique l'incrémentation de chaque séquence consécutive. Nous utilisons, pour établir une communication SIP avec le poste appelé, la fonction Dial(), dont le premier paramètre est le poste visé, et le second représente le temps maximal, en secondes, pendant lequel le poste sonnera. Ce dernier paramètre est optionnel ; s'il est omis, le poste sonnera tant que l'utilisateur n'a pas décroché.

À la façon des informations SIP, on recharge les paramètres relatifs au dialplan grâce à la CLI Asterisk :

asterisk*CLI> dialplan reload

Nos deux postes sont maintenant enregistrés et joignables, les appels peuvent être placés.

5. Dehors, le chaos

C'est à cet instant que tout se corse. Nos utilisateurs devront fatalement pouvoir joindre le monde extérieur, et dans notre cas d'école, cela se déroulera par le biais d'un (ou plusieurs) opérateur(s) de téléphonie.

Les combinaisons sont multiples. On pourra par exemple, dans le cas où votre fournisseur d'accès ne propose pas de facilités SIP, installer une carte de type FXO que sait parfaitement interfacer Asterisk. De telles cartes coûtent une dizaine d'euros sur les sites d'enchères connus. Une telle carte vous permettra de raccorder le RJ11 de votre ligne téléphonique classique et de le faire prendre en charge par Asterisk comme s'il s'agissait d'une interconnexion numérique.

Nous considèrerons que votre fournisseur d'accès est de type GratuiBoite, ou encore RougeBoite, ces derniers fournissant des accès SIP. Il est par ailleurs peu onéreux de nos jours de souscrire à une ligne téléphonique SIP ; chez un hébergeur low cost à trois lettres, un tel abonnement proposant quarante destinations gratuites coûte 1€ par mois.

Nous démarrons la configuration du raccordement à notre trunk SIP dans le fichier sip.conf. La première étape consiste à renseigner une directive d'enregistrement de notre IPBX vers le fournisseur de téléphonie publique. Nous utilisons pour cela la directive register, à placer dans la section [general] du fichier sip.conf. La syntaxe supportée par cette clé est la suivante :

register => [peer?][transport://]user[@domain][:secret[:authuser]]@
host[:port][/extension][~expiry]

Chez GratuiBoite, cela se traduit par :

register => utilisateur:motdepasse@freephonie.net

Chez RougeBoite :

register => +3399XXXXXXXXXX@ims.mnc010.mcc208.3gppnetwork.
org:motdepasse:NDIXXXXXXXXXX.LIBERTALK@sfr.fr@internet.p-cscf.sfr.
net:5064~3600

où XXXXXXXXXX représente votre numéro de téléphone public.

Et chez les low costeurs à trois lettres :

register => 003397994XXXX:motdepasse@sip.ovh.net

Afin de permettre le transit des appels, il est nécessaire de créer deux sections supplémentaires à notre configuration SIP, l'une pour les appels sortants, l'autre pour les appels entrants :

Pour GratuiBoite :

[freephonie-out]
type=peer
host=freephonie.net
username=XXXXXXXXXX
fromuser=XXXXXXXXXX
secret=motdepasse
fromdomain=freephonie.net
[freephonie-in]
type=user
context=fromfree
host=freephonie.net

Pour la RougeBoite, c'est un peu plus compliqué. En effet, cet opérateur a fait basculer son infrastructure VoIP dans un monde aussi inutilement complexe que cauchemardesque à appréhender : l'IMS (IP Multimedia Subsystem). Sans s'attarder sur le sujet qui prendrait plusieurs numéros de ce magazine pour en faire le tour, IMS est supposément, je cite :

« une architecture standardisée Next Generation Network (NGN) pour les opérateurs de téléphonie, qui permet de fournir des services multimédias fixes et mobiles. » ©Wikipédia.

Pour vous donner un aperçu du sac de nœuds, observez en figure 5 le schéma d'architecture qu'on peut admirer sur la page dédiée à ce standard, toujours sur Wikipédia.

Fig. 5

Ouais. Ça pique.

Fort heureusement, de charitables âmes ont épluché et testé les diverses combinaisons pour enregistrer Asterisk sur un tel réseau et ont proposé les sections suivantes, légèrement modifiées par votre serviteur :

[sfr-out]
type=peer
fromdomain=ims.mnc010.mcc208.3gppnetwork.org
fromuser=+3399XXXXXXXXXX
defaultuser=NDIXXXXXXXXXX.LIBERTALK@sfr.fr
host=internet.p-cscf.sfr.net
insecure=invite,port
remotesecret=motdepasse
auth=NDIXXXXXXXXXX.LIBERTALK@sfr.fr:motdepasse@ims.mnc010.
mcc208.3gppnetwork.org
outboundproxy=internet.p-cscf.sfr.net:5064

nat=yes
[sfr-in]
type=user
fromdomain=ims.mnc010.mcc208.3gppnetwork.org
host=internet.p-cscf.sfr.net
insecure=invite,port
context=from-sfr
port=5064
nat=yes

Hormis les directives relatives à l'identification, on note une clé inconnue : insecure=. Ce paramètre vaut no par défaut et indique à Asterisk que toutes les connexions doivent être authentifiées. Ici, nous spécifions que la requête INVITE initiale ne sera pas authentifiée, et que nous ne vérifierons pas le port duquel elle provient.

Enfin, source de tous les maux, nous indiquons qu'il s'agit d'une connexion « natée ». En particulier, afin de simplifier l'établissement de la session RTP, la directive nat=yes met en place une forme de RTP symétrique, comprendre que le User Agent utilisera la même socket et le même port pour émettre et recevoir le RTP.

Mais ce n'est pas la fin de nos ennuis ; telle quelle, cette configuration permettra de placer des appels, mais toujours pas de faire transiter du son. En effet, la SDP fera toujours mention des IP privées de notre réseau interne. La méthode généralement utilisée et fonctionnelle avec la plupart des fournisseurs d'accès SIP consiste à ajouter les paramètres suivants dans la section [general] du fichier sip.conf :

localnet=192.168.0.0/255.255.0.0 ; notre sous réseau privé
externip=1.2.3.4 ; IP publique de la passerelle

Ce qui aura pour effet de masquer les IP incluses dans le sous-réseau localnet et de les remplacer, dans la SDP, par l'IP externip. Pour une raison qui m'est inconnue, ce mécanisme pourtant classique ne fonctionne pas avec l'opérateur RougeBox ; si cette configuration permet en effet d'émettre des appels, la réception, elle, ne fonctionne plus. J'ai contourné ce dysfonctionnement en ôtant les directives localnet et externip et en leur préférant la variable media_address, qui laisse intactes les informations VIA des entêtes SIP, mais remplace l'IP média présente dans la SDP par la valeur indiquée.

Cela nous donne une section [general] de cette forme :

context=default
register => +3399XXXXXXXXXX@ims.mnc010.mcc208.3gppnetwork.
org:motdepasse:NDIXXXXXXXXXX.LIBERTALK@sfr.fr@internet.p-cscf.sfr.net:5064~3600
allowguest=no ; IMPORTANT, n’autorisez pas les accès anonymes !
alwaysauthreject=yes ; On traite les erreurs de mot de passe comme des
utilisateurs inexistants
contactdeny=0.0.0.0/0.0.0.0 ; on refuse toute connexion
contactpermit=91.68.1.28/255.255.255.255 ; à part celles listées ici
contactpermit=192.168.1.0/255.255.255.0 ; et ici
contactpermit=192.168.2.0/255.255.255.0 ; et encore ici
media_address=1.2.3.4 ; l’adresse du proxy RTP

dtmfmode=rfc2833 ; les codes DTMF respectent la RFC2833
directmedia=no ; par défaut, on définit que les sessions média ne s’établissent
pas directement entre les terminaux
nat=no ; par défaut, on n’active pas de NAT

Un poste SIP est alors déclaré de cette façon :

[snom300]
type=friend ; le poste émet et reçoit
context=maison ; on utilise le contexte général "maison"
username=snom ; nom d’utilisateur
secret=unpasswordsuperbalaise ; mot de passe
host=dynamic ; le poste s’enregistre seul
directmedia=yes ; de base, pour les appels internes, les sessions RTP
s’établissent directement

6. Vous avez demandé le SAMU, ne quittez pas

Nous attaquons la partie à mon sens la plus intéressante de ce setup, le dialplan public et le routage conditionnel des appels.

Nous avons effleuré les possibilités offertes par le fichier extensions.conf. En vérité, c'est une stratégie complète et complexe que vous pouvez ici mettre en œuvre par le biais de fonctionnalités somme toute assez simples.

Assurons-nous en premier lieu que nous pouvons désormais passer et recevoir des appels. On compose classiquement un numéro de téléphone public en démarrant la séquence par un 0. C'est ce comportement que nous allons indiquer à Asterisk :

exten => _0.,1,Dial(SIP/sfr-out/${EXTEN})

Le premier paramètre d'une extension n'est pas obligatoirement un nombre ou une lettre ; en réalité, nous sommes en présence d'une forme de regexp. Précédemment, nous avons utilisé des numéros directs de postes, mais il est également possible d'utiliser des extensions spéciales, par exemple :

- i pour une extension invalide,

- s lorsque le numéro appelé n'est connu dans aucun contexte, c'est typiquement l'extension utilisée lors de la réception d'appels extérieurs,

- h raccrochage,

- t timeout.

Ou encore, et c'est le cas qui nous occupe ici, d'utiliser des patterns. Dans l'exemple ci-dessus, _0. signifie n'importe quel numéro qui commence par 0. Un tel numéro sera routé vers le trunk SIP sfr-out et c'est toute l'extension qui sera composée (${EXTEN}). Si l'on possédait un second opérateur que nous souhaitions utiliser uniquement pour les numéros à destination des États-Unis, nous aurions écrit :

exten => _001.,1,Dial(SIP/ovh-out/${EXTEN})

Mieux, si l'on souhaite choisir son opérateur en fonction du premier numéro entré :

exten => _6.,1,Dial(SIP/ovh-out/${EXTEN:1})
exten => _7.,1,Dial(SIP/freephonie-out/${EXTEN:1})
exten => _8.,1,Dial(SIP/sfr-out/${EXTEN:1})

Dans cet exemple, si le numéro tapé commence par 6, on passera par le trunk SIP ovh-out et on prendra soin d'effacer le premier chiffre du numéro composé (${EXTEN:1}). On devine qu'en composant 7 suivi d'un numéro de téléphone, on passera par freephonie-out et 8 par sfr-out.

Mais ce n'est pas tout ! Les extensions comprennent une symbolique assez simple mais très puissante :

- X un chiffre entre 0 et 9,

- Y un chiffre entre 1 et 9,

- Z un chiffre entre 2 et 9,

- . équivaut à [a-zA-Z0-9]+ en regexp,

- ! équivaut à [a-zA-Z0-9]* en regexp.

Ces séquences sont obligatoirement préfixées par le symbole _. Voici quelques exemples de séquences possibles :

- _XX1234 matchera [0-9][0-9]1234 en regexp,

- _Z. matchera [2-9].+ en regexp,

- _001! matchera 001.* en regexp.

Les possibilités sont énormes.

Nous sommes en mesure de placer des appels extérieurs, mais quid des appels entrants ? Nous l'avons évoqué un peu plus haut, c'est l'extension spéciale s qui nous permettra de gérer ce cas :

[from-sfr]
exten => s,1,Dial(SIP/snom&SIP/samsungs4)

Comprendre que lorsqu'un appel est reçu dans le contexte from-sfr, on fait sonner les terminaux snom et samsungs4. Arrêtez de regarder votre mobile, on doit encore voir deux-trois trucs.

6.1 « Il est aux cabinets »

Mais le nirvana, le summum de la classe américaine, réside dans le fait de totalement informatiser ces appels, gérer de façon fine des appels entrants, et c'est précisément ce que nous permet la puissance du dialplan Asterisk.

Premièrement, il nous est possible de « brancher » un contexte sur un autre. Ceci est très simplement fait grâce à la fonction Goto(). Par exemple, si je souhaite renvoyer tout appel entrant vers un SVI (Serveur Vocal Interactif), je remplace notre extension précédente par :

exten => s,1,Goto(ivr,s,1)

et crée un nouveau contexte ivr, traduction anglaise de SVI, mais le nom du contexte n'a aucune importance :

exten => s,1,Playback(message_d_accueil)

À noter, par défaut les fichiers son disponibles pour Asterisk sont localisés dans /var/lib/asterisk/sounds ; le paquet asterisk de pkgsrc les place dans /usr/pkg/libdata/asterisk/sounds/en/. Cet emplacement est évidemment configurable dans le fichier asterisk.conf.

L'intérêt du message sonore est évidemment d'attendre un choix de l'utilisateur appelant, ceci est fait à l'aide de la fonction WaitExten() :

[ivr]
exten => s,1,Answer()
exten => s,n,Playback(message_d_accueil)
exten => s,n,WaitExten()

On choisit ensuite le routage de l'appel à l'aide du numéro tapé par l'appelant :

exten => 1,1,Goto(callhome,s,1) ; si l’appelant tape 1
exten => 2,1,Goto(spam,s,1) ; s’il tape 2
[callhome]
exten => s,1,Dial(SIP/snom&SIP/samsungs4)
exten => s,1,Hangup()
[spam]
exten => s,1,Playback(spam)
exten => s,n,Hangup()

Aah oui, ça commence à prendre forme hein ?

7. Yé né souis pas oune nouméro !

Laissez-moi finir ce tour d'horizon en vous introduisant le concept d'AGI ou Asterisk Gateway Interface. Asterisk peut s'interfacer sur bon nombre de langages familiers : C, Python, Perl, PHP, Shell...

Si les possibilités du couple dialplan / AGI sont pratiquement infinies, j'aimerais m'appuyer sur un exemple glané en ligne que j'ai utilisé pour filtrer les appels entrants, chez moi.

L'auteur utilise le système de synthèse vocale de Google, et plus précisément un script AGI, pour « parler » à travers son SVI. L'article étant parfaitement bien écrit, je ne paraphraserai pas l'auteur sur ces colonnes et vous invite à découvrir sa prose, mais vous exposerai simplement une application de ces possibilités à travers un morceau de mon fichier extensions.conf :

[from-sfr]
exten => s,1,Goto(ivr,s,1)
[ivr]
exten => s,1,Answer()
exten => s,n,agi(googletts.agi,"Pour parler a iMil, tapez 1",fr);
exten => s,n,agi(googletts.agi,"Televendeur, tapez 2.",fr);

exten => s,n,WaitExten()
exten => 1,1,Goto(callhome,s,1)
exten => 2,1,Goto(spam,s,1)

Si ce serveur vocal suffit à bloquer la plupart des tentatives de ventes de fenêtres, certains se sont essayés à appuyer sur 2, et ont eu le plaisir d'écouter le fichier spam.gsm fourni par défaut dans Asterisk, qui propose une foultitude de produits tels que Vi4gr4, Montres Rxxlex, amitié avec un prince Lybien, ou élargissement de l'entre-jambe.

Ouais, ça m'amuse.

8. On s'rappelle ?

Ce survol du monde fabuleux de la téléphonie libre a simplement effleuré les infinies possibilités que propose cet incroyable outil qu'est Asterisk, et pour finir en beauté, sachez que l'excellent livre « Asterisk, The Definitive Guide » est sous licence Creative Common et consultable en ligne, téléchargeable et bien évidemment achetable. À l'aide de ce dernier, qui vit depuis de nombreuses années, vous aurez le loisir de plonger dans des configurations épatantes qui dépassent de très loin les modestes pistes de réflexion exposées dans cet article.

Tags : Asterisk, VoIP