Implémentation de la fonction ‘Follow Me’ dans Asterisk avec PortaBilling
Oussama Hammami, 2010-04-26
Switzernet
Au moment d’authentification de client et l’autorisation de l’appel (RADIUS), PortaBilling renvoi la réponse ainsi qu’une liste de routage.
Dans notre cas on a plusieurs fournisseurs et pour chaque appel PortaBilling applique nos tarifs et renvoi une liste de routage triée par rapport au prix.
Réponse RADIUS du PortaBilling :
. . .
h323-ivr-in=PortaBilling_Routing:@;g-hunt=seq
h323-ivr-in=PortaBilling_Routing:@;g-hunt=skip;expires=300;credit-time=433140;patience=20
h323-ivr-in=PortaBilling_Routing: +41216912818@XXX.249.15.9;cli=+41215500329
h323-ivr-in=PortaBilling_Routing: +41216912818@XXX.249.15.3;cli=+41215500329
h323-ivr-in=PortaBilling_Routing: 0041216912818@XXX.168.45.4;cli=0041215500329;rtpp=1
h323-billing-model=0
h323-ivr-in=Tariff:business
h323-ivr-in=PortaBilling_CLI:41215500329
h323-ivr-in=MOH:1
h323-ivr-in=PortaBilling_CompleteNumber:41216912818
h323-ivr-in=PortaBilling_Auth_CLD:41216912818
h323-ivr-in=PortaBilling_Auth_Reseller_CLD:41216912818
h323-ivr-in=DURATION:433140
h323-return-code=0
h323-currency=CHF
h323-credit-time=433140
h323-preferred-lang=en
. . .
Si la fonction ‘Follow-Me’ est activée dans le compte de la destination appelée alors le PortaBilling renvoi les listes de routage des destinations configurées dans ce compte.
Exemple de compte dont la fonction ‘Follow-Me’ est activé
|
Réponse RADIUS du PortaBilling en appelant ce compte:
. . .
h323-ivr-in=PortaBilling_Routing:@;g-hunt=seq
h323-ivr-in=PortaBilling_Routing: 41215040306@91.121.70.119;expires=0;credit-time=-1;forward-on-fail=yes;rtpp=3;moh=1;patience=20
h323-ivr-in=PortaBilling_Routing: @;g-hunt=seq;credit-time=-1;bill-to=41215040306;access-code=FOLLOWME;rtpp=3;moh=1;patience=20
Destination 1 (D1)
h323-ivr-in=PortaBilling_Routing: @;g-hunt=skip;expires=5;credit-time=433140;bill-to=41215040306;auth-cld=41216912818;access-code=FOLLOWME;
h323-cid=1;patience=20
h323-ivr-in=PortaBilling_Routing: +41216912818@XXX.249.15.9;cli=+41215500329
h323-ivr-in=PortaBilling_Routing: +41216912818@XXX.249.15.3;cli=+41215500329
h323-ivr-in=PortaBilling_Routing: 0041216912818@XXX.168.45.4;cli=0041215500329;rtpp=1
Destination 2 (D2)
h323-ivr-in=PortaBilling_Routing: 41215500311@XXX.23.242.200;expires=20;auth-cld=41215500311
Destination 3 (D3)
h323-ivr-in=PortaBilling_Routing: 41215500327@XXX.121.66.202;expires=7;auth-cld=41215500327
h323-billing-model=0
h323-ivr-in=Tariff:business
. . .
Ces listes de routage sont groupées par la destination ‘auth-cld’ et ‘expires’ qui présente la durée de la sonnerie (le temps pendant lequel la déviation va sonner) pour chaque destination alors si cette durée est écoulé sans avoir une réponse, le serveur SIP (de l’appelant) doit passer à la destination suivante pas l’URI suivant car si c’est le cas la durée de la sonnerie sera n fois la vrai durée où n présente le nombre de routage possible.
On a constaté que PortaSip ne respecte pas la durée de la sonnerie envoyé par PortaBilling et voici un exemple dont ‘expires’ égale à 5 secondes :
PortaSip a routé l’appel vers tous les URI avec une durée ‘expires’ de 5 secondes alors qu’il doit router vers cette destination qu’une seule fois.
On se basant sur le projet GPL de PortaOne, on a développé une version plus avancée d’un client RADIUS pour Asterisk.
Il est écrit entièrement en Perl, il est donc 100% portable et facile à comprendre. Les attributs RADIUS sont utilisés selon ‘Cisco Voice VSA Guide de mise en œuvre’ ce qui offre une compatibilité avec de nombreuses plateformes de facturation (PortaBilling, PowerRadius).
Ci-dessous le résultat d’un appel vers un compte dont la fonction ‘Follow-Me’ mais cette fois l’appelant est enregistrée sur un Asterisk qui utilise notre solution :
Asterisk a routé appel qu’une seul fois vers cette destination ce qui est conforme avec la durée configurée dans le compte utilisant ‘Follow-Me’.
Le service de ‘Follow-Me’ n’est pas pour le moment récursif. Ainsi, un téléphone SIP A peut transmettre sa demande à un téléphone SIP B, mais B ne peut pas transférer cet appel à un autre numéro.
Le script agi-rad-auth-2.pl se charge de la communication avec le PortaBilling via RADIUS, il analyse la réponse RADIUS reçus et crée une liste de routage sous la forme des variable Asterisk en utilisant le module Perl ‘Asterisk::AGI’. Asterisk route l’appel selon cette liste dans le contexte ‘routing’. Dans le fichier ‘extension.conf’, il n’a y pas un contexte spéciale pour ‘Follow-Me', le contexte ‘routing’ exécute le route de ce qui a été crée par le script perl.
agi-rad-auth-2.pl
. . .
my @XSUM;
$AGI->set_variable('NumRoutes', $numroutes);
for ($i=0;$i<$numroutes;$i++) {
my $cli=$auth_cli;
$cli=@clis[$i] if defined @clis[$i];
$AGI->verbose(" -- AGI set route $i: @routes[$i] CLI: $cli",4);
if (@Join_Expire[$i])
{
@XSUM[@Join_Expire[$i]]=0 if (!defined @XSUM[@Join_Expire[$i]]);
$AGI->set_variable("XROUTE_@Join_Expire[$i]_@XSUM[@Join_Expire[$i]]", @routes[$i]);
$AGI->set_variable("XEXP_@Join_Expire[$i]_@XSUM[@Join_Expire[$i]]", @Expire[$i]);
$AGI->set_variable("XCLI_@Join_Expire[$i]_@XSUM[@Join_Expire[$i]]", $cli);
@XSUM[@Join_Expire[$i]]++;
}
}
my $k=0;
foreach my $vl (@XSUM)
{
if ($vl)
{
$AGI->set_variable("XSUM_$k", $vl);
}
$k++;
}
$AGI->set_variable("XTOT", $k);
. . .
La liste de routage est présenté par un tableau bidimensionnel dont le nom est ‘XROUTE_${II}_${KK}’
Exemple :
XROUTE_1_0 = +41216912818@XXX.249.15.9
XROUTE_1_1 = +41216912818@XXX.249.15.3
XROUTE_1_2 = 0041216912818@XXX.168.45.4
XROUTE_2_0 = 41215500311@XXX.23.242.200
XROUTE_3_0 = 41215500327@XXX.121.66.202
Extension.conf :
. . .
[auth-inbound]
exten => _X.,1,NoOp(-- Inbound Authentication --)
exten => _X.,n,Gosub(h323-id,${EXTEN},1)
exten => _X.,n,Set(CDR(accountcode)=${SIPCHANINFO(recvip)})
exten => _X.,n,Set(MYCALLID=${SIPCALLID})
exten => _X.,n,Set(CDR(CustomerType)=PeerAuth)
exten => _X.,n,agi,agi-rad-auth-2.pl|Routing=SIP&AuthorizeBy=Account&Password=XXXXXX&H323_ID=${H323_ID}&CustomerType=PeerAuth
exten => _X.,n,Goto(routing,${EXTEN},1)
[sip-auth]
exten => _X.,1,NoOp(-- SIP Authentication --)
exten => _X.,n,Gosub(h323-id,${EXTEN},1)
exten => _X.,n,Set(SIP_Authorization=${SIP_HEADER(Proxy-Authorization)})
exten => _X.,n,Set(CDR(CustomerType)=SipAuth)
exten => _X.,n,agi,agi-rad-auth-2.pl|Routing=SIP&AuthorizeBy=SIP&IfFailed=DoNotHangup&H323_ID=${H323_ID}&CustomerType=SipAuth
exten => _X.,n,Set(CDR(accountcode)=${SIP_Username})
exten => _X.,n,Set(CDR(disconnect-reason)=${h323-return-code})
exten => _X.,n,GotoIf($[ ${h323-return-code} = 1 | ${h323-return-code} = 2 ]?Invalid-Account,${EXTEN},1)
exten => _X.,n,GotoIf($[ ${h323-return-code} = 4 | ${h323-return-code} = 5 | ${h323-return-code} = 6 | ${h323-return-code} = 7 ]?blocked,${EXTEN},1)
exten => _X.,n,Goto(routing,${EXTEN},1)
[routing]
exten => _X.,1,Set(II=1)
exten => _X.,n(loop),NoOp()
exten => _X.,n,Set(KK=0)
exten => _X.,n,Set(SUM_ROUTE=${XSUM_${II}})
exten => _X.,n(loop1),NoOp()
exten => _X.,n,Set(TARGET_ROUTE=${XROUTE_${II}_${KK}})
exten => _X.,n,Set(TARGET_EXPIRE=${XEXP_${II}_${KK}})
exten => _X.,n,Set(TARGET_CLI=${XCLI_${II}_${KK}})
exten => _X.,n,Set(CALLERID(all)=${TARGET_CLI} <${TARGET_CLI}>)
exten => _X.,n,Set(MYDNID=${CUT(TARGET_ROUTE,@,1)})
exten => _X.,n,Set(VDNID=${CUT(MYDNID,/,2)})
exten => _X.,n,Ringing
exten => _X.,n,Dial(${IF($[ ${CUT(TARGET_ROUTE,@,2)} = ${NAS_IP_Address}]?${MYDNID}:${TARGET_ROUTE})},${TARGET_EXPIRE})
exten => _X.,n,Set(KK=$[${KK}+1])
exten => _X.,n,GotoIf($[$["${DIALSTATUS}" = "BUSY"] | $["${DIALSTATUS}" = "NOANSWER"]]?end1)
exten => _X.,n,GotoIf($[${KK} < ${SUM_ROUTE} ]?loop1:end1)
exten => _X.,n(end1),NoOp()
exten => _X.,n,Set(II=$[${II}+1])
exten => _X.,n,GotoIf($[${II} < ${XTOT} ]?loop:end)
exten => _X.,n(end),NoOp()
exten => _X.,n,Hangup(17)
. . .
- Télécharger agi-rad-auth-2.zip.
- Installer à l’aide de cpan :
cpan[1]> install Crypt::CBC
cpan[2]> install Crypt::DES
cpan[3]> install Authen::Radius
cpan[4]> install Asterisk::AGI
- Ajouter dans module.conf :
load => res_agi.so
- Copiez le fichier ‘agi-rad-auth-2.pl’dans le répertoire AGI d’Asterisk, habituellement ‘/usr/share/asterisk/agi-bin/’ (défini dans asterisk.conf)
- Modifier ‘extensions.conf’ en utilisant l'exemple inclus dans le zip téléchargé.
http://switzernet.com/public/100427-how-to-follow-me/
http://search.cpan.org/~jamesgol/asterisk-perl-0.09/lib/Asterisk/AGI.pm
https://cisco.com/en/US/docs/ios/12_3/security/configuration/guide/scgrdat3.pdf
http://www.voip-info.org/tiki-index.php?page=PortaOne+Radius+auth
http://www.portaone.com/news/news_detail.php?ID=1394
http://us.elite-school.com/modules/article/view.article.php/12/c15
http://www.voip-info.org/wiki/index.php?page_id=1780&tk=69b374fe850ea5e9cb39&comments_page=1