Archive for the ‘voip’ Category

Asterisk talk at IBM

Saturday, September 29th, 2007

This week took place the Innovation in Software Engineering Latin America Symposium at IBM Guadalajara. I participated with an Asterisk session giving an overview of the Asterisk capabilities with AEL, AGI and AMI. Also I mentioned how it was possible to run Asterisk on an IBM System i Linux LPAR effectively converting the System i in a powerful IP PBX. I plan to give the same talk, but with some adjustments at ENLi the next month.

Rapid VoIP Application Development on Linux

Overflow in AppConference

Sunday, September 2nd, 2007

Some weeks ago I got an offer for a free VoIPSurfer license. Why? just because of a patch I did one year ago for an issue I had, the patch helped the guy who seems to own voipsurfer, so, it is kind of open source Karma. The offering made me remember the interesting issue I had and I thought today it is a good day to write about it. Some background information can be found in this thread where I discussed with some Asterisk developers the issue: http://lists.digium.com/pipermail/asterisk-dev/2006-November/024616.html

Asterisk, as a software PBX, supports conferencing. However, default implementation in 1.2.x versions did not support native audio mixing. Asterisk default conferencing application is “app_meetme” and it works only if you have zaptel hardware available or if zaptel driver zt_dummy.ko is installed, otherwise it cannot work because meetme() use zt_conf functionality. Because of this I started looking for alternatives that did not require zaptel. I found app_conference which at that time was part of the IaxClient project, now it seems to be an independent project.

Anyway, I started doing some testing and kaboom!, Asterisk crashed when one of our IAX2 servers joined the conference. Running valgrind lead me to find code like this:

void mix_slinear_frames( char *dst, const char *src, int samples )
{

        if ( dst == NULL ) return ;

        if ( src == NULL ) return ;

        int i, val ;

        for ( i = 0 ; i < samples ; ++i )
        {

                val = ( (short*)dst )[i] + ( (short*)src )[i] ;

                if ( val > 0×7fff )
                {
                        ( (short*)dst )[i] = 0×7fff - 1 ;
                        continue ;
                }

                else if ( val < -0×7fff )
                {
                        ( (short*)dst )[i] = -0×7fff + 1 ;
                        continue ;
                }

                else
                {
                        ( (short*)dst )[i] = val ;
                        continue ;
                }
        }

        return ;
}

This is a typical function where we’re in buffer overflow danger. It receives 2 buffers as arguments and just 1 len. So better for the caller to be sure that both src and dst have enough data to read/write from/to. I quickly found a call like this:

// allocate a mix buffer which fill large enough memory to
// hold a frame, and reset it’s memory so we don’t get noise
char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ;

memset( cp_listenerBuffer, 0×0, AST_CONF_BUFFER_SIZE ) ;

// point past the friendly offset right to the data

cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ;

// reset the spoken list pointer
cf_spoken = frames_in ;

// really mix the audio
for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next )
{

    //
    // if the members are equal, and they
    // are not null, do not mix them.
    //
    if (
      ( cf_send->member == cf_spoken->member )
       && ( cf_send->member != NULL )
    )
    {

        // don’t mix this frame
    }
    else if ( cf_spoken->fr == NULL )
    {

        ast_log( LOG_WARNING, “unable to mix conf_frame with null ast_framen” ) ;
    }
    else
    {

        // mix the new frame in with the existing buffer
        mix_slinear_frames( cp_listenerData, (char*)( cf_spoken->fr->data ), cf_spoken->fr->samples);
     }
}

So let’s look closely to the call. The first argument passed is cp_listenerData, a pointer to a brand new voice frame where all the mixing of the conference audio sources will be stored. As we can see in the code, the buffer is of a fixed length AST_CONF_BUFFER_SIZE, but since the pointer is advanced AST_FRIENDLY_OFFSET, then the effective length is AST_CONF_BUFFER_SIZE - AST_FRIENDLY_OFFSET. Let’s see some interesting defines:

// 160 samples 16-bit signed linear
#define AST_CONF_BLOCK_SAMPLES 160

// 2 bytes per sample ( i.e. 16-bit )
#define AST_CONF_BYTES_PER_SAMPLE 2

// 320 bytes for each 160 sample frame of 16-bit audio
#define AST_CONF_FRAME_DATA_SIZE 320

// 1000 ms-per-second / 20 ms-per-frame = 50 frames-per-second
#define AST_CONF_FRAMES_PER_SECOND ( 1000 / AST_CONF_FRAME_INTERVAL )

// account for friendly offset when allocating buffer for frame
#define AST_CONF_BUFFER_SIZE ( AST_CONF_FRAME_DATA_SIZE + AST_FRIENDLY_OFFSET )

So AST_CONF_BUFFER_SIZE is 320 + AST_FRIENDLY_OFFSET, since we don’t really care about the asterisk friendly offset since *real* data must start after this offset, we can say our buffer is 320 bytes long. Where this number comes from? Well, some assumptions are made to calculate that number:

1. Telephony most common sampling rate is 8000 samples per second.
2. Audio frames will be 20ms long.
3. Each sample will be encoded using 16 bits ( “slinear” format ).

Given those assumptions we can then calculate: At 8000 samples per second, 20ms audio frame will be made of

( 8000 samples per second * ( 20ms / 1000ms per second) )

that is 160 samples in one 20ms audio frame. Since each of those samples will be encoded with 16 bits, we have a audio frame byte length of:

( 160 samples * 16 bits per sample / 8 bits per byte ), that is 320 bytes, hence #define AST_CONF_FRAME_DATA_SIZE 320

Assumption of 8000 samples per second is an Asterisk requirement, currently Asterisk does not support other sampling rate. 16 bits per sample is assumed because the mixing will be done in slinear format, so this is ok too. However, 20ms audio frames is a *bad* assumption. Some common phones ( Linksys SPA? ) provide an option in their configuration page to change the RTP frame size, and that caused the crash. How many bytes does a 30ms frame has?

( 8000 samples per second * ( 30ms / 1000ms per second) )

that is 240 samples, each one of those samples of 2 bytes ( 16 bits ) give us a frame size of 480 bytes!, far more than the considered 320 bytes, and kaboom! we have a buffer overflow that could lead in the best case to a DoS attack.

Asterisk smoothers to the rescue!

Asterisk smoothers is a way to take variant size frames as input and return frames of single size. So, we can feed the smoother with 480 bytes frames or whichever size we get and the smoother will return us a frame of the size we want ( 320 bytes in this case ).

struct ast_smoother *ast_smoother_new(int size);
int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap);
struct ast_frame *ast_smoother_read(struct ast_smoother *s);
ast_smoother_free(struct ast_smoother *s);

So, I used this interface to smooth each frame with more samples ( hence size ) than required for the slinear audio mixing. Each time a new frame was read from a conference member I added this verification and partial fix code:

/* create the smoother if were receiving more samples than needed */
if ( AST_CONF_BLOCK_SAMPLES < fr->samples ) {

  if ( member->inSmoother == NULL ) {
    /* calculate bytes per sample */

    ast_log(LOG_DEBUG,%s frame has %d samples in %d bytesn”, member->channel_name, fr->samples, fr->datalen);

    float bytes_per_sample = ( (float)fr->datalen / (float)fr->samples );

    ast_log(LOG_DEBUG,%s frame has %f bytes per samplen”, member->channel_name, bytes_per_sample);

    float new_frame_len    = ( AST_CONF_BLOCK_SAMPLES * bytes_per_sample );
    /* WARNING, currently iLBC codec is not fully supported, sound is still choppy */

    if ( fr->subclass == AST_FORMAT_ILBC ) {
      ast_log(LOG_DEBUG, “ILBC format only accepts datalen multiple of 50, so make it happyn”);

      if ( new_frame_len < 50 ) {
        new_frame_len = 50;
      }
    }

    ast_log(LOG_DEBUG,%s new frame size is %fn”, member->channel_name, new_frame_len);

    member->inSmoother   = ast_smoother_new(new_frame_len);
  }
  ast_smoother_feed(member->inSmoother, fr);

  while ( ( sfr = ast_smoother_read( member->inSmoother ) ) ) {

    conf_frame* cfr = create_conf_frame( member, member->inFrames, sfr ) ;

    if ( cfr == NULL )
    {
      ast_log( LOG_ERROR, “unable to malloc conf_framen” ) ;

      return -1 ;
    }
    add_member_frame(member, cfr);
  }
} else {

  conf_frame* cfr = create_conf_frame( member, member->inFrames, fr ) ;

  add_member_frame(member, cfr);
}

Messy, but hey! it compiled! ship it! ( I don’t remember why in the hell I used float for bytes_per_sample, but im sure I had a good reason?? )

So, what I did was just calculate the proper frame size when more samples than needed were received, so, when converted to slinear, the frame will result in 240 bytes as required by mix_slinear_frame and it will not overflow mixing the audio properly.

Anyway, I still got a problem I did not solve at that time. For some reason iLBC codec only accepted frame sizes multiple of 50, and since slinear mixing required 240 ( not a multiple of 50 ) voice sounded choppy :(

I guess some improvements were made to app_conference since I used it last time, so I will test it with Asterisk 1.4 and let’s see how it goes …

I’ll post results later,

Blog - Asterisk Unicall

Saturday, August 4th, 2007

Con la intenciòn de ayudar un poco mas con el tema de Asterisk Unicall y R2 (libmfcr2) abrì un blog por separado, en inglès
.

http://www.moythreads.com/astunicall/

Unicall para Asterisk 1.4

Sunday, February 25th, 2007

Despues de una pequeña discusión en asterisk-users , decidí poner manos a la obra sobre un TODO que ya tenía un par de meses en mi lista. Adaptar el driver de unicall para Asterisk 1.4, dado que steve underwood no ha tenido tiempo/interés de hacerlo. Así que hace un par de semanas me tomé un sábado, y con la ayuda de tato de neocenter.com al fin quedó listo para ser probado ( nosotros solo probamos con un par de llamadas ).

Los fuentes pueden ser encontrados aqui:

Unicall para Asterisk 1.4

se ven,

Asterisk Manager Interface PHP extension

Friday, December 8th, 2006

Ha pasado algo de tiempo desde mi ultimo post, pero he andado algo ocupado. Por estos dias ya empece a escribir ( al fin ) mi extensión en C para el lenguaje PHP, esta extensión le dará soporte a PHP para conectarse directamente y sin escalas al API del manager de Asterisk. En linux ya compila y funciona al 80%. Necesito terminar ese 20% restante y darle soporte para windows.

El API general para PHP es:

resource ami_connect(string host, string user, string passwd)
bool ami_send_action(resource ami, string action, [array data])
array ami_read(resource ami, int timeout)
bool ami_attach(resource ami, string event, callback user_callback)
bool ami_detach(resource ami, string event)
int ami_queue_action(resource ami, string action, [array data], [callback user_callback])
void ami_loop(resource ami)
void ami_disconnect(resource ami)

CONSTANTS

AMI_KILL_LOOP
AMI_EXIT_LOOP

Y este es un script que he estado usando para probar:

http://moy.ivsol.net/scripts/ami.php

Es interesante empezar a conocer mas a fondo como funciona el zend engine. Ya con algo mas de experiencia explicare a detalle como hacer una extensión, de momento solo quiero hacer mención que dentro del zend engine, todas las variables son representadas como una estructura de C llamada zval. Asi que por ejemplo, para recibir una variable como argumento usualmente se hace algo como esto:

zval *my_argument; // declaramos el apuntador a la variable PHP

int my_length; /* aqui el zend engine pondra la longitud del argumento, en este caso es una cadena */

zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &my_argument, &my_length);

de esta forma tenemos el argumento enviado por el usuario y la longitud de la cadena, el argumento “s” es una cadena de formateo, es necesario para especificar de que tipo debe ser el zval, en este caso una cadena.

ahila…

Aplicaciones y Funciones para Asterisk

Wednesday, September 13th, 2006

Leyendo Asterisk-Dev me encontre con una pregunta sobre la arquitectura interna de Asterisk. Como yo pase por esas dudas hace un año mas o menos, decidi escribir una contestación en donde describo el Proceso En Asterisk de Una Llamada con Tecnologia SIP
Unos minutos después, llego otro correo con información muy valiosa donde se describe detalladamente la creación de funciones nativas y aplicaciones en C para Asterisk. Muy recomendable:

http://www.lobstertech.com/doc/ast-12-func/

Por cierto, en ese articulo encontré el piropo mas geek que jamas había escuchado o leido:

“Hey baby, let me be your derivative so I can stay tangent to your curves”

Asterisk Masquerade

Friday, September 1st, 2006

Durante el desarrollo de nuestro framework para la construccion de modulos de ruteo para Asterisk (Y en un futuro FreeeSwitch), he tenido que crear algunos parches para nuevos eventos necesarios en Asterisk. Uno de ellos es este:

http://bugs.digium.com/view.php?id=7840

Este nuevo evento reporta cuando Asterisk hace masquerading sobre un canal. Pero esto trae la pregunta que es masquerade? Que son los canales de Asterisk? Para quienes deseen entenderlo, la siguiente es una breve descripcion:

Asterisk es, en primera instancia, un conmutador. Y que hace un conmutador?, efectivamente “conmuta”, sin embargo se han puesto a pensar que es lo que conmuta?? Si nos vamos a http://www.rae.es/ encontraremos que las definiciones son:

1. adj. Que conmuta.

2. m. Fís. Dispositivo de los aparatos eléctricos que sirve para que una corriente cambie de conductor.

3. m. Arg., Col., El Salv., Guat. y Méx. centralita (ǁ telefónica).

Efectivamente, las 3 definiciones aplican a Asterisk, excepto por un detalle de la segunda. La segunda definición Asume que lo que se desea conmutar es el circuito portador de corriente electrica. Por que eso es lo que efectivamente sucede en un conmutador analógico. Veamos un ejemplo rudimentario de como funcionaria una llamada desde un domicilio particular, hasta la extensión.

1. El telefono particular es descolgado y la compañia telefónica (p.e Telmex) nos provee de tono de marcado, que no es mas que la consecuente corriente electrica debido al voltaje de la línea.

2. Marcamos numeros (DTMF, Dual Tone Multi Frequency), enviados como pulsos de corriente electrica de diferentes frequencias. Telmex rutea nuestra llamada hasta el número destino.

3. La llamada es recibida por el conmutador de la compañia X, hasta el momento, el conmutador solo conoce 1 circuito (por donde llego la llamada desde telmex) , muy posiblemente nos contestará el conmutador y pedira marquemos el número de extension.

4. Cuando marquemos el número de extensión, el conmutador abrira otro circuito para enviar la señal a la extension destino y conectará los dos circuitos.

En sintesis, un conmutador analógico conmuta circuitos. Pero dado que Asterisk no solo conecta circuitos de corriente, sino también “circuitos” de protocolos sobre IP (H323, IAX2, SIP), se nombra a los circuitos, canales (o sesiones en el lingo de FreeSwitch). Asi que cuando en Asterisk se recibe una llamada por una tarjeta Zaptel, se crea un canal especial de tipo Zap (o tal vez Unicall), si esta llamada desea ser terminada en una extensión SIP, entonces Asterisk crea un segundo canal, pero esta vez de tipo SIP, y los conecta!, igual que el conmutador analógico, permitirá que el audio fluya de SIP a Zap y viceversa.

Ahora, que es “Channel Masquerading”?
En realidad es un truco “sucio” de software para poder implementar funcionalidad como transferencias, pickup call y otras cosas que un PBX debe poder hacer. Para explicarlo veremos el ejemplo de como se implementa la funcionalidad de “pickup”. Esta funcionalidad implica que cuando una extension esta “sonando”, otra extensión puede marcar un número especial, comunmente “*8″ para “levantar” la llamada. Para esto Asterisk involucra al menos 2 canales, el de la extension que esta sonando, y el de la extensión que marco “*8″. Sin embargo, para que una extension este sonando usualmente es necesario que otro usuario haya marcado el numero de la extension, lo que involucraría un 3er canal, aqui asumiremos eso. Utilizaremos 3 personajes ficticios, Juanito, Pepito y Roberta.

Juanito marca desde su extensión el número de la extension de pepito, asi que Asterisk primero creo el canal para recibir la llamada de Juanito, quien tiene una extension IAX, asi que el primer canal creado es IAX, al interpretar el número marcado por Juanito, Asterisk decide que el número corresponde a la extensión de Pepito, la cual es una extensión SIP, asi que Asterisk crea un segundo canal, esta vez tipo SIP, este nuevo canal es el que se encarga de hacer que la extensión de Pepito timbre, mientras timbra, Asterisk envia al canal IAX el sonido del timbrado de la extension SIP. Sin embargo, como pepito no contesta, entra Roberta, la secretaría, en acción.

Roberta decide que levantará la llamada, asi que marca “*8″, Asterisk crea un nuevo canal de acuerdo a la extensión de roberta, que es tipo H323. Al recibir el número, Asterisk decide que es el código usado para levantar llamadas, asi que busca dentro de todos los canales activos, el primer canal que se encuentre en estado “Ringing”, encuentra la extensión de pepito, sin embargo, la arquitectura de Asterisk no permite, de forma sencilla, “cortar” el flujo de una aplicación, en este caso la aplicación es Dial() y esta aplicación es la que tiene el control del canal de Juanito y el de Pepito, en un solo hilo de ejecución (thread), la llamada de Roberta entro a Asterisk en otro thread, como hacemos entonces para que el canal de Roberta remplace el canal de Pepito??, usamos “channel masquerading”!.

Esto es, Asterisk intercambiará rapida, y suciamente, un monton de apuntadores y valores pertenecientes al canal de Pepito para que ahora apunten a el canal de Roberta (a pesar de ser de diferentes tipos, gran cantidad de datos son genéricos) y entonces Roberta pueda hablar con Juanito!, el canal de pepito es “flageado” como “zombie” y es finalmente colgado por Asterisk.

Espero haber sido lo suficientemente claro. Y bueno, el pequeño parche que hice reporta este evento al manager para que nuestro sistema daemon se entere de este cambio y haga exactamente lo mismo (intercambiar los canales), pero mucho mas sencillo, desde PHP :)

se ven…

Traducción al inglés y actualización de documento sobre MFC/R2

Friday, August 18th, 2006

A petición de agunas personas en asterisk-users, traduje al inglés el documento que escribí sobre MFCR2 con Asterisk. Un nuevo documento sobre MFCR2 con Asterisk actualizado y traducido puede ser encontrado aqui:

http://www.moythreads.com/unicall/mfcr2-asterisk-unicall-0.2-english.pdf

Ruteadores Con Asterisk y otros

Friday, August 18th, 2006

En junio estuve en el Ciclo de Conferencias de Software Libre en Cuernavaca impartiendo un par de talleres. Prometi postear el material pero la desidia hizo muy buen su trabajo y hasta ahora estoy cumpliendo, jeje, y sobre todo por que dentro de unas horas daré nuevamente un taller relacionado, ahora en el CONSOL. Asi que mejor de una vez pongo las ligas.

Material Ruteador PHP con Asterisk

De una vez… aprovechando el viaje, aqui esta el material de la instalacion y configuracion de Asterisk.

Material Instalacion Asterisk

se ven …

CDR en SQLite3 para Asterisk

Tuesday, April 11th, 2006

La semana pasada fué aceptado para ser probado un nuevo CDR handler para Asterisk que escribí para soportar la base de datos SQLite3. Son cambios simples al anterior manejador existente para SQLite2, pero nos permitirá mantener nuestro Asterisk lo mas apegado posible a la distribución oficial.

Si alguien se encuentra interesado en probar esta funcionalidad, chequen SQLite3 CDR