Implémentation des appels entrants simultanés dans Asterisk

Oussama Hammami, 2011-07-24

Switzernet

 

 

L’Asterisk est incapable de gérer les appels entrants vers plusieurs téléphones SIP qui utilisent le même compte SIP, en effet il route l’appel vers le dernier téléphone enregistré. De même notre serveur de routage (Porta-billing) renvoi lors de l’authentification (RADIUS) l’adresse IP de serveur SIP qui a reçu le dernier enregistrement.

Scénario

Notre serveur Asterisk utilise le module Real-Time (MySQL), Il remonte l’enregistrement vers son DBA. SI le client possède plus un enregistrement valide dans la table location2 le serveur DBA l’ajoute dans la table multiple_ua qui sera par la suite répliqué vers tous les Astrads.

 

La table location2 contiendra tous les enregistrements de tous les Astrad grâce à la synchronisation assuré par le script exec-regitration.pl, ce qui implique que tous les clients qui utilisent plus un téléphone SIP sont dans la table multiple_ua.

 

Lors d’un appel entrant l’Asterisk demande le routage en appelant la procédure set_route() et la fonction get_route().

CREATE FUNCTION `get_route`(USR VARCHAR(80)) RETURNS text

NOT DETERMINISTIC

BEGIN

IF (SELECT route FROM route_multiple_ua WHERE username=USR) IS NOT NULL THEN

  RETURN (SELECT route FROM route_multiple_ua WHERE username=USR);

ELSE

  RETURN CONCAT('SIP/',USR);

END IF;

END |

DELIMITER ;

 

DROP PROCEDURE IF EXISTS `set_route`|

CREATE PROCEDURE `set_route`(IN USR VARCHAR(80), IN ipsource VARCHAR(15))

BEGIN

DECLARE ROUTE text DEFAULT '';

DECLARE done INT DEFAULT 0;

DECLARE tdomain varchar(15) default NULL;

DECLARE tipaddr varchar(15) default NULL;

DECLARE tport smallint(5) unsigned default NULL;

DECLARE prev_ipaddr varchar(15) default NULL;

DECLARE prev_port smallint(5) unsigned default NULL;

DECLARE fg  INT DEFAULT 0;

DECLARE ID_ INT DEFAULT 0;

DECLARE myIP VARCHAR(15);

DECLARE local_call INT DEFAULT 0;

DECLARE cur CURSOR FOR SELECT domain,ipaddr,port FROM multiple_ua WHERE username=USR;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

DROP TABLE IF EXISTS tmpdomain;

CREATE TEMPORARY TABLE tmpdomain (

  domain varchar(15) NOT NULL DEFAULT '' ,

  PRIMARY KEY  (`domain`)

);

DELETE FROM route_multiple_ua WHERE username=USR;

IF (SELECT 1 FROM  multiple_ua WHERE domain=ipsource AND username=USR LIMIT 1) THEN

  SET local_call=1;

END IF;

IF (SELECT COUNT(username) FROM multiple_ua WHERE username=USR ) > 1 THEN

  SET myIP=(SELECT value FROM sipconfig WHERE name='ipaddr');

  SET ID_=(SELECT value FROM sipconfig WHERE name='ua_id');

  OPEN cur;

  read_loop: LOOP

    FETCH cur INTO tdomain,tipaddr,tport;

    IF done THEN

      LEAVE read_loop;

    END IF;

    IF (tdomain=myIP) THEN

      IF (fg=0) THEN

            SET fg=1;

                SET prev_ipaddr=tipaddr;

                SET prev_port=tport;

          ELSEIF (fg=1) THEN

            SET fg=2;

            INSERT INTO sippeers2 (name,host,ipaddr,port) VALUES (CONCAT('UA',ID_),prev_ipaddr,prev_ipaddr,prev_port) ON DUPLICATE KEY UPDATE host=prev_ipaddr, ipaddr=prev_ipaddr, port=prev_port;

                SET ROUTE=CONCAT(ROUTE,'&SIP/',USR,'@UA',ID_);

                SET ID_=ID_+1;

                IF (ID_>1000) THEN

                  SET ID_=1;

                END IF;

            INSERT INTO sippeers2 (name,host,ipaddr,port) VALUES (CONCAT('UA',ID_),tipaddr,tipaddr,tport) ON DUPLICATE KEY UPDATE host=tipaddr, ipaddr=tipaddr, port=tport;

                SET ROUTE=CONCAT(ROUTE,'&SIP/',USR,'@UA',ID_);

                SET ID_=ID_+1;

                IF (ID_>1000) THEN

                  SET ID_=1;

                END IF;

          ELSE

            INSERT INTO sippeers2 (name,host,ipaddr,port) VALUES (CONCAT('UA',ID_),tipaddr,tipaddr,tport) ON DUPLICATE KEY UPDATE host=tipaddr, ipaddr=tipaddr, port=tport;

                SET ROUTE=CONCAT(ROUTE,'&SIP/',USR,'@UA',ID_);

                SET ID_=ID_+1;

                IF (ID_>1000) THEN

                  SET ID_=1;

                END IF;

          END IF;

        ELSEIF local_call=0 AND (SELECT 1 FROM tmpdomain WHERE domain=tdomain LIMIT 1) IS NULL THEN

          SET ROUTE=CONCAT(ROUTE,'&SIP/',USR,'@',tdomain);

          INSERT INTO tmpdomain VALUES (tdomain);

        END IF;

  END LOOP;

  CLOSE cur;

  IF (fg=1) THEN

    SET ROUTE=CONCAT(ROUTE,'&SIP/',USR);

  ELSE

    UPDATE sipconfig SET value=ID_ WHERE name='ua_id';

  END IF;

  IF ROUTE NOT LIKE '' THEN

    INSERT IGNORE INTO route_multiple_ua VALUES (USR,SUBSTRING(ROUTE,2));

  END IF;

END IF;

END |

DELIMITER ;

 

La procédure set_route() ajoute temporairement des entrées dans la table sippeers2 qui corresponds aux URIs du compte appelé et il insère dans la table route_multiple_ua la valeur de la commande DIAL qui sera utilisé par l’asterisk pour appelé ce client.

extensions.conf

[MyRoute]

exten => _[*0-9]!,1,NoOp()

exten => _[*0-9]!,n,ExecIf($[ "${ActivePrivacy}" = "1" ],SetCallerPres,prohib_not_screened) ; this might not be needed --- needs further testing

exten => _[*0-9]!,n,Set(multipleUA=0)

exten => _[*0-9]!,n,GotoIf($["${connid}" = ""]?MyRoute-error,${EXTEN},1)

exten => _[*0-9]!,n,MYSQL(Query resultid ${connid} CALL set_route(\'${PBCLD}\'\,\'${SIPCHANINFO(recvip)}\'))

exten => _[*0-9]!,n,MYSQL(Clear ${resultid})

exten => _[*0-9]!,n,MYSQL(Query resultid ${connid} SELECT get_route(\'${PBCLD}\'))

exten => _[*0-9]!,n,MYSQL(Fetch fetchid ${resultid} myROUTE)

exten => _[*0-9]!,n,GotoIf($[ "${myROUTE}"="NULL" | "${myROUTE}"="" ]?MyRoute-error,${EXTEN},1)

exten => _[*0-9]!,n,MYSQL(Clear ${resultid})

exten => _[*0-9]!,n,MYSQL(Disconnect ${connid})

exten => _[*0-9]!,n,System(/bin/echo "'${myROUTE}'" >> /var/log/asterisk/astrad.log)

exten => _[*0-9]!,n,DIAL(${myROUTE},30)

exten => _[*0-9]!,n,Hangup(${HANGUPCAUSE})

exten => _[*0-9]!,n,Set(CDR(Hangupcause)=Hangupcause:${HANGUPCAUSE})

[MyRoute-error]

exten => _[*0-9]!,1,NoOp()

exten => _[*0-9]!,n,DIAL(SIP/${PBCLD},30)

exten => _[*0-9]!,n,Hangup(${HANGUPCAUSE})

exten => _[*0-9]!,n,Set(CDR(Hangupcause)=Hangupcause:${HANGUPCAUSE})

[check_ua]

exten => _[*0-9]!,1,NoOp()

exten => _[*0-9]!,n,MYSQL(Connect connid localhost ${dbuser} ${dbpass} ${dbname})

exten => _[*0-9]!,n,GotoIf($["${connid}" = ""]?error)

exten => _[*0-9]!,n,MYSQL(Query resultid ${connid} SELECT\ 1\ FROM\ multiple_ua\ WHERE\ username=\'${PBCLD}\' LIMIT 1)

exten => _[*0-9]!,n,MYSQL(Fetch fetchid ${resultid} multipleUA)

exten => _[*0-9]!,n,ExecIf($[ "${multipleUA}"="NULL" | "${multipleUA}"="" ],Set,multipleUA=0)

exten => _[*0-9]!,n,MYSQL(Clear ${resultid})

exten => _[*0-9]!,n,ExecIf($[ ${multipleUA} = 0 ],MYSQL,Disconnect ${connid})

exten => _[*0-9]!,n(error),Return()

 

[routing]

exten => _[*0-9]!,1,NoOp()

exten => _[*0-9]!,n,ExecIf($[ "${CALLERID(name)}" = "Anonymous" | "${CALLERID(name)}" = "anonymous"],Set,ActivePrivacy=1)

exten => _[*0-9]!,n,ExecIf($[ ${multipleUA} = 0 ],Gosub,check_ua,${EXTEN},1)

exten => _[*0-9]!,n,GotoIf($[ ${multipleUA} = 1 ]?MyRoute,${EXTEN},1)

exten => _[*0-9]!,n(loop),NoOp()

exten => _[*0-9]!,n,GotoIf($[ ${II} = 1 ]?SingleRoute)

exten => _[*0-9]!,n,Playback(the-next,noanswer)

exten => _[*0-9]!,n(SingleRoute),Set(KK=0)

exten => _[*0-9]!,n,Set(SUM_ROUTE=${XSUM_${II}})

exten => _[*0-9]!,n(loop1),NoOp()

exten => _[*0-9]!,n,Set(TARGET_ROUTE=${XROUTE_${II}_${KK}})

exten => _[*0-9]!,n,Set(TARGET_EXPIRE=${XEXP_${II}_${KK}})

exten => _[*0-9]!,n,Set(TARGET_CLI=${XCLI_${II}_${KK}})

exten => _[*0-9]!,n,Set(CALLERID(all)=${TARGET_CLI} <${TARGET_CLI}>)

exten => _[*0-9]!,n,Set(MYDNID=${CUT(TARGET_ROUTE,@,1)})

exten => _[*0-9]!,n,Set(VDNID=${CUT(MYDNID,/,2)})

exten => _[*0-9]!,n,Set(ROUTEIP=${CUT(TARGET_ROUTE,@,2)})

exten => _[*0-9]!,n,GotoIf($[ "${ActivePrivacy}" != "1" ]?noprivacy)

exten => _[*0-9]!,n,GotoIf($[  "${ROUTEIP}" != "${NAS_IP_Address}" ]?extprivacy)

exten => _[*0-9]!,n,SetCallerPres(prohib_not_screened) ; this might not be needed --- needs further testing

exten => _[*0-9]!,n,Goto(noprivacy)

exten => _[*0-9]!,n(extprivacy),NoOp()

exten => _[*0-9]!,n,SIPAddHeader(Remote-Party-ID:"${CALLERID(num)}" <sip:${CALLERID(num)}@${NAS_IP_Address}>\;privacy=full\;screen=yes)

exten => _[*0-9]!,n(noprivacy),NoOp()

exten => _[*0-9]!,n,Dial(${IF($[ ${CUT(TARGET_ROUTE,@,2)} = ${NAS_IP_Address}]?${MYDNID}:${TARGET_ROUTE})},${TARGET_EXPIRE})

exten => _[*0-9]!,n,Set(CDR(Hangupcause)=Hangupcause:${HANGUPCAUSE})

exten => _[*0-9]!,n,Set(KK=$[${KK}+1])

exten => _[*0-9]!,n,GotoIf($[ "${DIALSTATUS}" = "BUSY" | "${DIALSTATUS}" = "NOANSWER" | "${DIALSTATUS}" = "CANCEL" ]?end1)

;exten => _[*0-9]!,n,GotoIf($[ ${HANGUPCAUSE} = 1 | ${HANGUPCAUSE} = 3 | ${HANGUPCAUSE} = 27 | ${HANGUPCAUSE} = 28 | ${HANGUPCAUSE} = 31 | ${HANGUPCAUSE} = 88 | ${HANGUPCAUSE} >= 90 | "${DIALSTATUS}" = "BUSY" | "${DIALSTATUS}" = "NOANSWER" | "${DIALSTATUS}" = "CANCEL" ]?end1)

exten => _[*0-9]!,n,GotoIf($[${KK} < ${SUM_ROUTE} ]?loop1:end1)

exten => _[*0-9]!,n(end1),NoOp()

exten => _[*0-9]!,n,Set(II=$[${II}+1])

exten => _[*0-9]!,n,GotoIf($[${II} <= ${XTOT} ]?loop:end)

exten => _[*0-9]!,n(end),NoOp()

exten => _[*0-9]!,n,Goto(Error-Info,${EXTEN},1)

Exemple

DBA

mysql> select * from multiple_ua where username=41215500327;

+-------------+---------------+--------------+-------+

| username    | domain        | ipaddr       | port  |

+-------------+---------------+--------------+-------+

| 41215500327 | 91.121.151.58 | 81.62.23.131 | 50874 | <- Astrad15

| 41215500327 | 91.121.151.58 | 81.62.23.131 | 50877 | <- Astrad15

| 41215500327 | 94.23.225.212 | 81.62.23.131 | 50863 | <- Astrad1

+-------------+---------------+--------------+-------+

3 rows in set (0.00 sec)

Astrad1

mysql> select get_route(41215500327) ;

+-----------------------------------------------+

| get_route(41215500327)                        |

+-----------------------------------------------+

| SIP/41215500327@91.121.151.58&SIP/41215500327 |

+-----------------------------------------------+

1 row in set (0.00 sec)

 

mysql> select * from location3 where username=41215500327;

+-------------+--------------+-------+---------------------+---------------------+

| username    | ipaddr       | port  | expires             | register            |

+-------------+--------------+-------+---------------------+---------------------+

| 41215500327 | 81.62.23.131 | 50863 | 2011-07-24 18:18:48 | 2011-07-24 18:16:18 |

+-------------+--------------+-------+---------------------+---------------------+

1 row in set (0.00 sec)

Astrad15

mysql> select get_route(41215500327);

+-------------------------------------------+

| get_route(41215500327)                    |

+-------------------------------------------+

| SIP/41215500327@UA25&SIP/41215500327@UA26 |

+-------------------------------------------+

1 row in set (0.00 sec)

 

mysql> select * from location3 where username=41215500327;

+-------------+--------------+-------+---------------------+---------------------+

| username    | ipaddr       | port  | expires             | register            |

+-------------+--------------+-------+---------------------+---------------------+

| 41215500327 | 81.62.23.131 | 50874 | 2011-07-24 18:21:19 | 2011-07-24 18:18:49 |

| 41215500327 | 81.62.23.131 | 50877 | 2011-07-24 18:20:34 | 2011-07-24 18:18:04 |

+-------------+--------------+-------+---------------------+---------------------+

2 rows in set (0.00 sec)

 

mysql> select * from sippeers2 where name in ('UA25','UA26');

+------+------+--------------+-----------+----------+--------------+-------+------------+----------+-----------+------+

| id   | name | host         | context   | insecure | ipaddr       | port  | regseconds | username | md5secret | nat  |

+------+------+--------------+-----------+----------+--------------+-------+------------+----------+-----------+------+

| 7494 | UA25 | 81.62.23.131 | forbidden | no       | 81.62.23.131 | 50874 |       NULL | NULL     | NULL      | yes  |

| 7495 | UA26 | 81.62.23.131 | forbidden | no       | 81.62.23.131 | 50877 |       NULL | NULL     | NULL      | yes  |

+------+------+--------------+-----------+----------+--------------+-------+------------+----------+-----------+------+

2 rows in set (0.00 sec)

Référence

Tester la commande Dial d’Asterisk en précisant l’adresse IP et le port du téléphone

http://switzernet.com/3/public/110630-test-dial-for-incomming-call/

 

Master Mysql Astrad DBA V004

http://switzernet.com/3/public/110722-dba-v4/

 

Astrad V8

http://switzernet.com/3/public/110724-astrad-V8/

 

Versions du Master MySQL-Astrad  (DBA)

http://switzernet.com/3/public/110317-db3-versions/

 

Astrad Versionning

http://switzernet.com/3/public/110126-astrad-versions/