Probando Red Hat Enterprise Virtualization

Pues siguiendo con la virtualización aquí mostramos una pantalla del manager de Red Hat para su hypervisor, de su producto Red Hat Enterprise Virtualization.

El manager para el control de las maquinas virtuales.

Un maquina virtual con Windows 7.

Server 2003 en una maquina virtual.

Tiene algunos detalles pero sin duda es una buena alternativa (en la parte económica) contra otros productos como VMware o Citrix.

Virtualización con KVM

KVM es una solución para implementar virtualización completa con Linux sobre hardware x86.
Así que aprovechando los recursos con los que cuenta el área de sistemas haremos uso de un servidor ocioso.

El equipo donde realizaremos las pruebas necesarias es un PowerEdge T110 con la distribución Debian Lenny instalada a partir de su versión net-install, por lo cual contaremos con un sistema básico pero mas que suficiente para nuestro cometido. A medida que necesitemos paquetes los instalaremos según se requiera, cabe resaltar que el modulo KVM ya se encuentra incluido en nuestro kernel instalado con nuestra distribución.

Antes que nada debemos verificar que el procesador del equipo soporte la virtualización.

$ egrep '^flags.*(vmx|svm)' /proc/cpuinfo

http://www.linux-kvm.org/page/FAQ#How_can_I_tell_if_I_have_Intel_VT_or_AMD-V.3F

Si el comando anterior nos arroja algún resultado nuestro procesador es idóneo para la virtualización en caso contrario no podremos hacer uso de KVM.

instalamos los paquetes necesarios.

# aptitude install kvm kvm-source

En la medida que se configuran los paquetes de manera automática en debian, si recibimos las siguientes lineas es por que no habilitamos en el BIOS de nuestro equipo la opción de virtualización.

Setting up kvm (72+dfsg-5~lenny5) ...
FATAL: Error inserting kvm_intel (/lib/modules/2.6.26-2-686/kernel/arch/x86/kvm/kvm-intel.ko)
: Operation not supported
Module kvm_intel failed to load failed!
invoke-rc.d: initscript kvm, action "start" failed.

http://www.linux-kvm.org/page/FAQ#.22KVM:_disabled_by_BIOS.22_error

kvm disabled by bios

En el equipo con el que estoy realizando esto entramos a la configuración del BIOS, en el menú de “Processor Settings” encontraremos la opción llamada “Virtualization Technology” la cual deberemos habilitar para poder trabajar con la virtualización por hardware.

Instalamos qemu para crear nuestros discos virtuales (y muchas cosas mas).

# aptitude install qemu

Si pretendemos que nuestras maquinas virtuales sean a nivel de red como un dispositivo físico mas debemos instalar el siguiente paquete y configurar ciertos parámetros en nuestro sistema anfitrión para poner su red en modo “bridge”.

# aptitude install bridge-utils

Debemos realizar unos cambios en nuestro archivo de configuración de red para modificar las interfaces, el contenido de mi archivo “interfaces” (/etc/network/interfaces) es el siguiente:

# 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
auto eth0
iface eth0 inet manual

auto br0
iface br0 inet static
        address 192.168.1.161
        netmask 255.255.255.0
        network 192.168.1.0
        broadcast 192.168.1.255
        gateway 192.168.1.254
        bridge_ports eth0
        bridge_fd 9
        bridge_hello 2
        bridge_maxage 12
        bridge_stp off

Reiniciamos los servicios de red.

# /etc/init.d/networking restart

Hecho esto al asignarle una dirección IP a nuestra maquina virtual esta sera como cualquier otro dispositivo en la red.

Realizaremos una instalación para ver que tal luce la nueva beta de Red Hat en su versión 6, la cual podemos descargar desde el siguiente enlace: ftp://ftp.redhat.com/pub/redhat/rhel/beta/6/i386/iso/RHEL6.0-20100414.0-AP-i386-DVD1.iso

Creamos un disco duro de unos 30 GB.

$ qemu-img create disk.RHEL -f qcow2 30G
Formatting 'disk.RHEL', fmt=qcow2, size=31457280 kB

Iniciamos la maquina virtual indicando que inicie desde cdrom a partir de una imagen ISO para instalar el sistema, asignamos 1256 MB de memoria RAM, le indicamos modelo y parámetros de tarjeta de red, teclado en español e iniciar un servicio VNC para visualizar la maquina virtual.

# kvm -hda disk.RHEL -cdrom ../RHEL6.0-20100414.0-AP-i386-DVD1.iso -boot d -m 1256 -k es -net nic,vlan=0,model=rtl8139 -net tap,vlan=0 -vnc :0

Terminada la instalación ahora iniciamos desde la imagen del disco duro.

# kvm -hda disk.RHEL -m 1256 -k es -net nic,vlan=0,model=rtl8139 -net tap,vlan=0 -vnc :0

Sistema RHEL instalado y navegando en Internet.

Un SUSE en plena instalación.

Dominio y alias en OpenSIPS

Siguiendo el propósito original de estos escritos, continuamos con las configuraciones del proxy OpenSIPS. El post anterior trataba de autenticar los usuarios haciendo uso de MySQL, ahora agregaremos la función de manejar dominios y alias, por lo tanto deberemos realizar modificaciones a nuestro archivo de configuración el cual simbólicamente denominaremos opensips.cfg.3, el cual mostramos a continuación.

Tambien vamos a cambiar una linea en el archivo opensipsctlrc y modificaremos el primer parámetro definiendo el dominio que vamos a utilizar de manera predeterminada al agregar nuevos usuarios: “SIP_DOMAIN=trantor.demerzel.org”

####### Global Parameters #########
debug=3
log_stderror=no
log_facility=LOG_LOCAL0

fork=yes
children=4

port=5060

/* uncomment and configure the following line if you want opensips to bind on a specific interface/port/proto (default bind on all available) */
#listen=udp:192.9.200.161:5060

####### Modules Section ########

#set module path
mpath="/usr/local/lib/opensips/modules/"

/* uncomment next line for MySQL DB support */
loadmodule "db_mysql.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "alias_db.so"
loadmodule "domain.so"
loadmodule "signaling.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "mi_fifo.so"
loadmodule "uri.so"
loadmodule "xlog.so"
loadmodule "acc.so"

# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 0)

# ----- usrloc params -----
modparam("usrloc", "db_mode",   0)

modparam("usrloc", "db_mode",   2)
modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")


# ----- uri params -----
modparam("uri", "use_uri_table", 0)
# ----- acc params -----
/* what sepcial events should be accounted ? */
modparam("acc", "early_media", 1)
modparam("acc", "report_ack", 1)
modparam("acc", "report_cancels", 1)
modparam("acc", "detect_direction", 0)
modparam("acc", "failed_transaction_flag", 3)
modparam("acc", "log_flag", 1)
modparam("acc", "log_missed_flag", 2)
modparam("acc", "db_flag", 1)
modparam("acc", "db_missed_flag", 2)

modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")
modparam("auth_db", "load_credentials", "")

modparam("alias_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")

modparam("domain", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")
modparam("domain", "db_mode", 1)

route{

	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483","Too Many Hops");
		exit;
	}

	if (has_totag()) {
		if (loose_route()) {
			if (is_method("BYE")) {
				setflag(1); # do accounting ...
				setflag(3); # ... even if the transaction fails
			} else if (is_method("INVITE")) {
				record_route();
			}
			route(1);
		} else {
			/* uncomment the following lines if you want to enable presence */
			if ( is_method("ACK") ) {
				if ( t_check_trans() ) {
					t_relay();
					exit;
				} else {
					exit;
				}
			}
			sl_send_reply("404","Not here");
		}
		exit;
	}

	if (is_method("CANCEL"))
	{
		if (t_check_trans())
			t_relay();
		exit;
	}

	t_check_trans();

	if (!(method=="REGISTER") && is_from_local())  /*multidomain version*/
        {
              if (!proxy_authorize("", "subscriber")) {
                      proxy_challenge("", "0");
                      exit;
              }
              if (!db_check_from()) {
                      sl_send_reply("403","Forbidden auth ID");
                      exit;
              }

              consume_credentials();
              # caller authenticated
        }


	if (loose_route()) {
		xlog("L_ERR",
		"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
		if (!is_method("ACK"))
			sl_send_reply("403","Preload Route denied");
		exit;
	}

	# record routing
	if (!is_method("REGISTER|MESSAGE"))
		record_route();

	# account only INVITEs
	if (is_method("INVITE")) {
		setflag(1); # do accounting
	}

	if (!is_uri_host_local())
	{
        	if(is_from_local()) {
			route(1);
	        } else {
			sl_send_reply("403","Not here");
        	}
	}

	if (is_method("PUBLISH"))
	{
		sl_send_reply("503", "Service Unavailable");
		exit;
	}

	if (is_method("REGISTER"))
	{
                if (!www_authorize("", "subscriber"))
                {
                      www_challenge("", "0");
                      exit;
                }

                if (!db_check_to())
                {
                      sl_send_reply("403","Forbidden auth ID");
                      exit;
                }

		if (!save("location"))
			sl_reply_error();

		exit;
	}

	if ($rU==NULL) {
		# request with no Username in RURI
		sl_send_reply("484","Address Incomplete");
		exit;
	}

        # apply DB based aliases (uncomment to enable)
        alias_db_lookup("dbaliases");

	# do lookup with method filtering
	if (!lookup("location","m")) {
		switch ($retcode) {
			case -1:
			case -3:
				t_newtran();
				t_reply("404", "Not Found");
				exit;
			case -2:
				sl_send_reply("405", "Method Not Allowed");
				exit;
		}
	}
	# when routing via usrloc, log the missed calls also
	setflag(2);
	route(1);
}

route[1] {
	# for INVITEs enable some additional helper routes
	if (is_method("INVITE")) {
		t_on_branch("2");
		t_on_reply("2");
		t_on_failure("1");
	}

	if (!t_relay()) {
		sl_reply_error();
	};
	exit;
}

branch_route[2] {
	xlog("new branch at $run");
}


onreply_route[2] {
	xlog("incoming replyn");
}


failure_route[1] {
	if (t_was_cancelled()) {
		exit;
	}
}

Continue reading “Dominio y alias en OpenSIPS”

Cambiar contraseña de root en un disco con LVM

Si se ha olvidado o se ignora la contraseña de root de algún equipo, en teoría es sencillo cambiar la contraseña para poder recuperar nuestro equipo, en este caso en especifico el disco esta particionado y usa LVM (Logical Volume Manager), por lo tanto el procedimiento tiene unos cuantos pasos mas a diferencia de si tuviéramos un sistema de partición tradicional. Para esto nos ayudaremos del live cd mas famoso últimamente, Ubuntu 9.10

Necesitamos instalar las herramientas necesarias para manejar los volúmenes lógicos.

ubuntu@ubuntu:~$ sudo aptitude install lvm2
Leyendo lista de paquetes... Hecho
Creando árbol de dependencias
Leyendo la información de estado... Hecho
Leyendo la información de estado extendido
Inicializando el estado de los paquetes... Hecho
Se instalarán los siguiente paquetes NUEVOS:
  lvm2 watershed{a}
0 paquetes actualizados, 2 nuevos instalados, 0 para eliminar y 246 sin actualizar.
Necesito descargar 387kB de ficheros. Después de desempaquetar se usarán 1114kB.
¿Quiere continuar? [Y/n/?] Y

Determinamos que grupo contiene nuestra partición física.

ubuntu@ubuntu:~$ sudo pvs
  PV         VG         Fmt  Attr PSize  PFree
  /dev/sda2  VolGroup00 lvm2 a-   74,41G 32,00M

Ahora podemos visualizar los volúmenes lógicos existente en VolGroup00

ubuntu@ubuntu:~$ sudo lvdisplay /dev/VolGroup00
  --- Logical volume ---
  LV Name                /dev/VolGroup00/LogVol00
  VG Name                VolGroup00
  LV UUID                FjQd7F-xxXz-Q2yd-OID7-eusj-y69S-ewCguZ
  LV Write Access        read/write
  LV Status              NOT available
  LV Size                73,50 GB
  Current LE             2352
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto

  --- Logical volume ---
  LV Name                /dev/VolGroup00/LogVol01
  VG Name                VolGroup00
  LV UUID                tGFOHq-LzOP-ppnP-d5FX-yNYj-Lu6y-fPso1L
  LV Write Access        read/write
  LV Status              NOT available
  LV Size                896,00 MB
  Current LE             28
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto

Activamos el grupo, de otra manera no podremos montar el volumen como si de una partición tradicional se tratara.

ubuntu@ubuntu:~$ sudo vgchange -a y
  2 logical volume(s) in volume group "VolGroup00" now active

Creamos el directorio donde montaremos el volumen logico.

ubuntu@ubuntu:~$ sudo mkdir /tmp/aguas/ (por Aguascalientes, de donde vino el servidor)

Montamos ahora si como si de una partición se tratara.

ubuntu@ubuntu:~$ sudo mount /dev/VolGroup00/LogVol00 /tmp/aguas/

Cambiamos el directorio raíz de / a /tmp/aguas para que los cambios que hagamos se reflejen en la partición montada.

ubuntu@ubuntu:/tmp/aguas$ sudo chroot /tmp/aguas/

Verificamos los directorios con los que cuenta el sistema montado.

bash-3.00# ls
bin	    home	misc	    proc	srv	    var
boot	    lib		mnt	    root	sys
dev	    lost+found	net	    sbin	tmp
etc	    media	opt	    selinux	usr
bash-3.00# ls home/
bash			      libpcap-0.9.4-4.fc5.i386.rpm
iftop-0.17-1.fc4.rf.i386.rpm  sistemas
iftop-0.17-1.fc5.rf.i386.rpm

Ahora si cambiamos la contraseña del superusuario.

bash-3.00# passwd root
Changing password for user root.
New UNIX password:
BAD PASSWORD: it is based on a dictionary word
Retype new UNIX password:
passwd: all authentication tokens updated successfully.
bash-3.00# exit

Reiniciamos el equipo y debemos poder entrar al sistema satisfactoriamente con la contraseña que cambiamos.

Mas información en el siguiente enlace: http://www.brandonhutchinson.com/Mounting_a_Linux_LVM_volume.html

Transferencia SIP

Una de las cosas mas sencillas en este mundo actual es transferir una llamada. Ya que andamos probando el proxy SIP al que hacemos mención, verificaremos que es lo que pasa cuando realizamos este procedimiento comparándolo con el que hace Asterisk.

Tenemos tres usuarios SIP, 1000, 1001 y 1002. El dispositivo asociado a cada uno respectivamente son un SPA941, SPA2102 y el softphone Zoiper, el mas importante de estos tres es el teléfono IP que gracias a su display podemos verificar el comportamiento que estamos buscando.

Vamos a poner un sencillo ejemplo para ilustrarlo, el cual de manera general sera el siguiente.

1001 recibe una llamada de 1002.
1001 inicia transferencia atendida con 1000.
1000 contesta la llamada y aparece en el CALLERID 1001.
1000 acepta la llamada.
1001 cuelga.
1000 tiene la llamada y el CALLERID cambia de 1001 a 1002.
1002 cambia su CALLERID ahora a 1000.

Vamos a realizar una primera llamada, marcamos del usuario 1002 al 1001.

Establecemos la llamada, y en la lista de contactos de nuestro Softphone se nos muestra que estamos hablando con el usuario 1001.

Ahora 1001 pone en espera a 1002, y le marca a 1000 para transferirle la llamada, en este paso en el display del teléfono asociado al usuario 1000, aparece que esta recibiendo una llamada de 1001, acepta la llamada se establece la llamada y le indican que le van a transferir al usuario 1002. 1001 transfiere la llamada y termina su participación en la llamada.

Y es aquí donde vemos el comportamiento deseado, después de un instante en el display del teléfono se muestra que estamos hablando ahora con el usuario 1002, y en el softphone de igual manera cambia de que ahora hablamos con el usuario 1000.

Con Asterisk no pasa esto, los estados de los teléfonos en su CALLERID no cambian, el teléfono 1002 se queda con el primer numero que marco y el teléfono 1000 mantiene en su pantalla el numero de donde recibió la transferencia.

¿Y cuál es el motivo de esto?,debido a detalles del protocolo SIP que esta implementado en Asterisk esta característica por el momento no se encuentra disponible. Mas información acerca de esto en un buen debate en la lista de asterisk-es en el siguiente hilo: CALLER ID en transferencias atendidas.

Y como anexo, se muestra la traza SIP de la transferencia capturada con Wireshark.
Continue reading “Transferencia SIP”

Autenticación en OpenSIPS con MySQL

En esta parte usaremos una base de datos para registrar mediante una contraseña nuestros usuarios SIP dando un tanto mas de seguridad a nuestro pequeño sistema, y de tener en tablas todas las preferencias de nuestros usuarios, dominios, direcciones y grupos como veremos consecuentemente.

Partimos del hecho de que contamos con nuestra base de datos MySQl correctamente instalada y con nuestra contraseña de root configurada. Debemos realizar ciertas modificaciones al archivo opensipsctlrc (/usr/local/etc/opensips/opensipsctlrc), el cual debe quedar de la siguiente forma:

trantor:/usr/local/etc/opensips# cat opensipsctlrc | grep -v "#" | sed -e '/^$/d'
 SIP_DOMAIN=192.168.1.161
 DBENGINE=MYSQL
 DBHOST=localhost
 DBNAME=opensips
 DBRWUSER=opensips
 DBRWPW="opensipsrw"
 DBROUSER=opensipsro
 DBROPW=opensipsro
 ALIASES_TYPE="DB"
 OSIPS_FIFO="/tmp/opensips_fifo"
trantor:/home/gabriel#

Creamos la base de datos.

trantor:/home/gabriel/SIP# opensipsdbctl create
MySQL password for root:
INFO: test server charset
INFO: creating database opensips ...
INFO: Core OpenSIPS tables succesfully created.
Install presence related tables? (y/n): y
INFO: creating presence tables into opensips ...
INFO: Presence tables succesfully created.
Install tables for imc cpl siptrace domainpolicy carrierroute userblacklist? (y/n): y
INFO: creating extra tables into opensips ...
INFO: Extra tables succesfully created.
trantor:/home/gabriel/SIP#

Cambiamos la configuración de opensips.cfg por la configuración que se muestra a continuación la cual simbólicamente denominaremos opensips.cfg.1.

####### Global Parameters #########
debug=3
log_stderror=no
log_facility=LOG_LOCAL0

fork=yes
children=4

port=5060

/* (default bind on all available) */
#listen=udp:192.168.1.161:5060

####### Modules Section ########

#set module path
mpath="/usr/local/lib/opensips/modules/"

/* uncomment next line for MySQL DB support */
loadmodule "db_mysql.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "signaling.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "mi_fifo.so"
loadmodule "uri.so"
loadmodule "xlog.so"
loadmodule "acc.so"

# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 0)

# ----- usrloc params -----
modparam("usrloc", "db_mode",   0)

modparam("usrloc", "db_mode",   2)
modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")


# ----- uri params -----
modparam("uri", "use_uri_table", 0)
# ----- acc params -----
/* what sepcial events should be accounted ? */
modparam("acc", "early_media", 1)
modparam("acc", "report_ack", 1)
modparam("acc", "report_cancels", 1)
modparam("acc", "detect_direction", 0)
modparam("acc", "failed_transaction_flag", 3)
modparam("acc", "log_flag", 1)
modparam("acc", "log_missed_flag", 2)
modparam("acc", "db_flag", 1)
modparam("acc", "db_missed_flag", 2)

modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")
modparam("auth_db", "load_credentials", "")


route{

	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483","Too Many Hops");
		exit;
	}

	if (has_totag()) {
		if (loose_route()) {
			if (is_method("BYE")) {
				setflag(1); # do accounting ...
				setflag(3); # ... even if the transaction fails
			} else if (is_method("INVITE")) {
				record_route();
			}
			route(1);
		} else {
			if ( is_method("ACK") ) {
				if ( t_check_trans() ) {
					t_relay();
					exit;
				} else {
					exit;
				}
			}
			sl_send_reply("404","Not here");
		}
		exit;
	}

	if (is_method("CANCEL"))
	{
		if (t_check_trans())
			t_relay();
		exit;
	}

	t_check_trans();

        if (!(method=="REGISTER") && from_uri==myself)
        {
              if (!proxy_authorize("", "subscriber")) {
                      proxy_challenge("", "0");
                      exit;
              }
              if (!db_check_from()) {
                      sl_send_reply("403","Forbidden auth ID");
                      exit;
              }

              consume_credentials();
              # caller authenticated
        }


	if (loose_route()) {
		xlog("L_ERR",
		"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
		if (!is_method("ACK"))
			sl_send_reply("403","Preload Route denied");
		exit;
	}

	# record routing
	if (!is_method("REGISTER|MESSAGE"))
		record_route();

	# account only INVITEs
	if (is_method("INVITE")) {
		setflag(1); # do accounting
	}
	if (!uri==myself)
	{
		append_hf("P-hint: outboundrn");
		route(1);
	}

	if (is_method("PUBLISH"))
	{
		sl_send_reply("503", "Service Unavailable");
		exit;
	}

	if (is_method("REGISTER"))
	{
                if (!www_authorize("", "subscriber"))
                {
                      www_challenge("", "0");
                      exit;
                }

                if (!db_check_to())
                {
                      sl_send_reply("403","Forbidden auth ID");
                      exit;
                }

		if (!save("location"))
			sl_reply_error();

		exit;
	}

	if ($rU==NULL) {
		# request with no Username in RURI
		sl_send_reply("484","Address Incomplete");
		exit;
	}

	# do lookup with method filtering
	if (!lookup("location","m")) {
		switch ($retcode) {
			case -1:
			case -3:
				t_newtran();
				t_reply("404", "Not Found");
				exit;
			case -2:
				sl_send_reply("405", "Method Not Allowed");
				exit;
		}
	}
	# when routing via usrloc, log the missed calls also
	setflag(2);
	route(1);
}

route[1] {
	# for INVITEs enable some additional helper routes
	if (is_method("INVITE")) {
		t_on_branch("2");
		t_on_reply("2");
		t_on_failure("1");
	}

	if (!t_relay()) {
		sl_reply_error();
	};
	exit;
}

branch_route[2] {
	xlog("new branch at $run");
}


onreply_route[2] {
	xlog("incoming replyn");
}


failure_route[1] {
	if (t_was_cancelled()) {
		exit;
	}
}

Iniciamos OpenSIPS.

trantor:~# opensipsctl start

INFO: Starting OpenSIPS :
INFO: started (pid: 28716)

Agregamos el par de usuarios 1000 y 1001, a la base de datos y registramos nuestros agentes SIP.

gabriel@trantor:~$ sudo opensipsctl add 1000 1000
new user '1000' added
gabriel@trantor:~$ sudo opensipsctl add 1001 1001
new user '1001' added

Mostramos la traza SIP del registro de un usuario almacenando previamente en nuestra base de datos.

U 192.168.1.162:5061 -> 192.168.1.161:5060
REGISTER sip:192.168.1.161 SIP/2.0.
Via: SIP/2.0/UDP 192.168.1.162:5061;branch=z9hG4bK-6bdbbdbc.
From: Bozada ;tag=ea98cc30d70f560fo1.
To: Bozada .
Call-ID: 5da1e386-fde63540@192.168.1.162.
CSeq: 32475 REGISTER.
Max-Forwards: 70.
Contact: Bozada ;expires=3600.
User-Agent: Linksys/SPA2102-3.3.6.
Content-Length: 0.
Allow: ACK, BYE, CANCEL, INFO, INVITE, NOTIFY, OPTIONS, REFER.
Supported: x-sipura.
.
U 192.168.1.161:5060 -> 192.168.1.162:5061
SIP/2.0 401 Unauthorized.
Via: SIP/2.0/UDP 192.168.1.162:5061;branch=z9hG4bK-6bdbbdbc.
From: Bozada ;tag=ea98cc30d70f560fo1.
To: Bozada ;tag=c97b4d1cb1f3d0da549e06a8d482ef63.be3a.
Call-ID: 5da1e386-fde63540@192.168.1.162.
CSeq: 32475 REGISTER.
WWW-Authenticate: Digest realm="192.168.1.161", nonce="4b85d15b000000039d8eac02cb35f405c16770fd04ba2424".
Server: OpenSIPS (1.6.1-notls (i386/linux)).
Content-Length: 0.
.
U 192.168.1.162:5061 -> 192.168.1.161:5060
REGISTER sip:192.168.1.161 SIP/2.0.
Via: SIP/2.0/UDP 192.168.1.162:5061;branch=z9hG4bK-896f8b1c.
From: Bozada ;tag=ea98cc30d70f560fo1.
To: Bozada .
Call-ID: 5da1e386-fde63540@192.168.1.162.
CSeq: 32476 REGISTER.
Max-Forwards: 70.
Authorization: Digest username="1000",realm="192.168.1.161",nonce="4b85d15b000000039d8eac02cb35f405c16770fd04ba2424",uri="sip:192.168.1.161",algorithm=MD5,response="5a8c46a3bd960218616aaed03240e631".
Contact: Bozada ;expires=3600.
User-Agent: Linksys/SPA2102-3.3.6.
Content-Length: 0.
Allow: ACK, BYE, CANCEL, INFO, INVITE, NOTIFY, OPTIONS, REFER.
Supported: x-sipura.
.
U 192.168.1.161:5060 -> 192.168.1.162:5061
SIP/2.0 200 OK.
Via: SIP/2.0/UDP 192.168.1.162:5061;branch=z9hG4bK-896f8b1c.
From: Bozada ;tag=ea98cc30d70f560fo1.
To: Bozada ;tag=c97b4d1cb1f3d0da549e06a8d482ef63.274d.
Call-ID: 5da1e386-fde63540@192.168.1.162.
CSeq: 32476 REGISTER.
Contact: ;expires=3600.
Server: OpenSIPS (1.6.1-notls (i386/linux)).
Content-Length: 0.
.

Y como un diagrama a veces vale mas, aquí mostramos la gráfica de la traza SIP anterior.

| 192.168.1.162     			| 192.168.1.161
|         Request: REGISTER             |
|(5061)   ------------------>  (5060)   |
|         Status: 401 Unauthorized      |
|(5061)     (5060)   |
|         Status: 200 OK                |
|(5061)   <------------------  (5060)   |

Realizamos nuevamente una sencilla llamada del usuario 1001 al 1000, y esta es la traza SIP de la llamada, la principal diferencia respecto al esquema anterior, es la parte de la autentificación.
En este caso al realizar el INVITE de la llamada, el servidor responde con mensaje SIP 407 (autenticación requerida), nuestro agente SIP responde y si son correctos los parámetros dejara realizar el INVITE iniciando la llamada, el resto de la traza SIP es igual a la del post anterior.

| 192.168.1.163     				| 192.168.1.161 (proxy)			| 192.168.1.162
|         INVITE SDP ( h261)            	|                   			|
|(5060)   ------------------>  (5060)   	|                   			|
|         407 Proxy Authentication Required	|                   			|
|(5060)     (5060)   	|                   			|
|         INVITE SDP ( h261)            	|                   			|
|(5060)   ------------------>  (5060)   	|                   			|
|         100 Giving a try              	|                   			|
|(5060)     (5061)   |
|                   				|         100 Trying			|
|                   				|(5060)   <------------------  (5061)   |
|                   				|         180 Ringing                   |
|                   				|(5060)   <------------------  (5061)   |
|         180 Ringing                   	|                   			|
|(5060)   <------------------  (5060)   	|                   			|
|                   				|         200 OK SDP ( h261)            |
|                   				|(5060)   <------------------  (5061)   |
|         200 OK SDP ( h261)            	|                   			|
|(5060)     (5060)   	|                   			|
|                   				|         ACK       			|
|                   				|(5060)   ------------------>  (5061)   |
|         BYE       				|                   			|
|(5060)   ------------------>  (5060)		|                   			|
|                   				|         BYE       			|
|                   				|(5060)   ------------------>  (5061)   |
|                   				|         200 OK    			|
|                   				|(5060)   <------------------  (5061)   |
|         200 OK    				|                   			|
|(5060)   <------------------  (5060)   	|                   			|

Mediante el siguiente comando podemos observar a los usuarios registrados en el servidor.

trantor:~# opensipsctl ul show
Domain:: location table=512 records=2
        AOR:: 1000
                Contact:: sip:1000@192.168.1.162:5061 Q=
                        Expires:: 2336
                        Callid:: 5da1e386-fde63540@192.168.1.162
                        Cseq:: 32476
                        User-agent:: Linksys/SPA2102-3.3.6
                        State:: CS_SYNC
                        Flags:: 0
                        Cflag:: 0
                        Socket:: udp:192.168.1.161:5060
                        Methods:: 5183
        AOR:: 1001
                Contact:: sip:1001@192.168.1.163:5060;transport=udp Q=
                        Expires:: 3596
                        Callid:: d35fe6bb7fc5df8b6c76521a80310ba6@0.0.0.0
                        Cseq:: 2
                        User-agent:: SIP Communicator 1.0 CVS-Wed_Feb_24_19-45-25_CST_2010
                        State:: CS_NEW
                        Flags:: 0
                        Cflag:: 0
                        Socket:: udp:192.168.1.161:5060
                        Methods:: 4294967295

Instalando OpenSIPS

OpenSIPS es una proxy SIP, registra y establece llamadas entre clientes SIP a continuación se mostrara la forma de instalarlo y configurarlo poco a poco, todo esto basándome en un libro recién publicado que trata sobre este tema el cual es Building Telephony Systems with OpenSIPS 1.6 de Flavio E. Goncalves.
Mas que nada es como un ejercicio para no olvidar los pasos y procedimientos.

El equipo a utilizar es mi propia portátil con Debian Lenny. Antes que nada necesitamos los siguientes paquetes:

# aptitude install gcc bison flex make openssl libmysqlclient-dev libradiusclient-ng2 libradiusclient-ng-dev mysql-server libxmlrpc-c3-dev

Descargamos, compilamos e instalamos con los módulos correspondientes.

$ wget http://opensips.org/pub/opensips/1.6.1/src/opensips-1.6.1-tls_src.tar.gz
$ tar xfz opensips-1.6.1-tls_src.tar.gz
$ cd opensips-1.6.1-tls
$ make all include_modules=”db_mysql aaa_radius”
$ make install include_modules=”db_mysql aaa_radius”

Los módulos y archivos de configuración se encuentran en /usr/local/lib/opensips/modules/ y /usr/local/etc/opensips/ respectivamente.

Con el archivo por defecto (/usr/local/etc/opensips/opensips.cfg) ya podemos iniciar un simple registro y realizar una llamada entre dos agentes SIP, sin embargo vamos a eliminar del archivo todo el código comentado e innecesario para así poder tener un archivo base mas claro el cual nombraremos simbólicamente opensips.cfg.0 el cual se muestra a continuación:

####### Global Parameters #########
debug=3
log_stderror=no
log_facility=LOG_LOCAL0

fork=yes
children=4

port=5060

#listen=udp:192.168.1.161:5060

####### Modules Section ########

#set module path
mpath="/usr/local/lib/opensips/modules/"

/* uncomment next line for MySQL DB support */
#loadmodule "db_mysql.so"
loadmodule "signaling.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "mi_fifo.so"
loadmodule "uri.so"
loadmodule "xlog.so"
loadmodule "acc.so"

# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 0)

# ----- usrloc params -----
modparam("usrloc", "db_mode",   0)
# ----- uri params -----
modparam("uri", "use_uri_table", 0)
# ----- acc params -----
/* what sepcial events should be accounted ? */
modparam("acc", "early_media", 1)
modparam("acc", "report_ack", 1)
modparam("acc", "report_cancels", 1)
modparam("acc", "detect_direction", 0)
modparam("acc", "failed_transaction_flag", 3)
modparam("acc", "log_flag", 1)
modparam("acc", "log_missed_flag", 2)
modparam("acc", "db_flag", 1)
modparam("acc", "db_missed_flag", 2)

route{

	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483","Too Many Hops");
		exit;
	}

	if (has_totag()) {
		if (loose_route()) {
			if (is_method("BYE")) {
				setflag(1); # do accounting ...
				setflag(3); # ... even if the transaction fails
			} else if (is_method("INVITE")) {
				record_route();
			}
			route(1);
		} else {
			if ( is_method("ACK") ) {
				if ( t_check_trans() ) {
					t_relay();
					exit;
				} else {
					exit;
				}
			}
			sl_send_reply("404","Not here");
		}
		exit;
	}

	if (is_method("CANCEL"))
	{
		if (t_check_trans())
			t_relay();
		exit;
	}

	t_check_trans();

	if (loose_route()) {
		xlog("L_ERR",
		"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
		if (!is_method("ACK"))
			sl_send_reply("403","Preload Route denied");
		exit;
	}

	# record routing
	if (!is_method("REGISTER|MESSAGE"))
		record_route();

	# account only INVITEs
	if (is_method("INVITE")) {
		setflag(1); # do accounting
	}
	if (!uri==myself)
	{
		append_hf("P-hint: outboundrn");
		route(1);
	}

	if (is_method("PUBLISH"))
	{
		sl_send_reply("503", "Service Unavailable");
		exit;
	}

	if (is_method("REGISTER"))
	{
		if (!save("location"))
			sl_reply_error();

		exit;
	}

	if ($rU==NULL) {
		# request with no Username in RURI
		sl_send_reply("484","Address Incomplete");
		exit;
	}

	# do lookup with method filtering
	if (!lookup("location","m")) {
		switch ($retcode) {
			case -1:
			case -3:
				t_newtran();
				t_reply("404", "Not Found");
				exit;
			case -2:
				sl_send_reply("405", "Method Not Allowed");
				exit;
		}
	}
	# when routing via usrloc, log the missed calls also
	setflag(2);
	route(1);
}

route[1] {
	# for INVITEs enable some additional helper routes
	if (is_method("INVITE")) {
		t_on_branch("2");
		t_on_reply("2");
		t_on_failure("1");
	}

	if (!t_relay()) {
		sl_reply_error();
	};
	exit;
}

branch_route[2] {
	xlog("new branch at $run");
}


onreply_route[2] {
	xlog("incoming replyn");
}


failure_route[1] {
	if (t_was_cancelled()) {
		exit;
	}
}

Iniciamos OpenSIPS.

# opensipsctl start

INFO: Starting OpenSIPS :
INFO: started (pid: 6359)

Registramos el usuario 1000 contraseña 1000 y usuario 1001 contraseña 1001 a la IP de nuestro equipo corriendo el proxy, en estos momentos aun no introducimos la autenticación de usuarios, por lo tanto debemos registrarnos sin problemas.

Aquí mostramos la traza SIP al registrar el primer usuario con un ATA de Linksys, vemos el register que realiza y el ok enviado por el servidor, esto lo capturamos con ngrep. (ngrep –p –q –W byline port 5060 > register1000C.txt)

U 192.168.1.162:5061 -> 192.168.1.161:5060
REGISTER sip:192.168.1.161 SIP/2.0.
Via: SIP/2.0/UDP 192.168.1.162:5061;branch=z9hG4bK-37cd1d0c.
From: Bozada ;tag=711253242c005ba6o1.
To: Bozada .
Call-ID: 3746d82e-460188b6@192.168.1.162.
CSeq: 20974 REGISTER.
Max-Forwards: 70.
Contact: Bozada ;expires=3600.
User-Agent: Linksys/SPA2102-3.3.6.
Content-Length: 0.
Allow: ACK, BYE, CANCEL, INFO, INVITE, NOTIFY, OPTIONS, REFER.
Supported: x-sipura.
.
U 192.168.1.161:5060 -> 192.168.1.162:5061
SIP/2.0 200 OK.
Via: SIP/2.0/UDP 192.168.1.162:5061;branch=z9hG4bK-37cd1d0c.
From: Bozada ;tag=711253242c005ba6o1.
To: Bozada ;tag=c97b4d1cb1f3d0da549e06a8d482ef63.3318.
Call-ID: 3746d82e-460188b6@192.168.1.162.
CSeq: 20974 REGISTER.
Contact: ;expires=3600.
Server: OpenSIPS (1.6.1-notls (i386/linux)).
Content-Length: 0.

Registrados los dos usuarios realizamos una simple llamada desde el usuario 1000 al 1001, y aquí mostramos lo que pasa mediante SIP para establecer la llamada, entre el ultimo ACK y el primer BYE esta el flujo RTP. (capturado con Wireshark)

| 192.168.1.162     			| 192.168.1.161 (proxy)			| 192.168.1.163
|         INVITE SDP ( telephone-event)	|          				|
|(5061)   ------------------>  (5060)   |                   			|
|         100 Giving a try              |                   			|
|(5061)     (5060)   |
|                   			|         180 Ringing                   |
|                   			|(5060)   <------------------  (5060)   |
|         180 Ringing                   |                   			|
|(5061)   <------------------  (5060)   |                   			|
|                   			|         200 OK SDP ( 97)              |
|                   			|(5060)   <------------------  (5060)   |
|         200 OK SDP ( 97)              |                   			|
|(5061)     (5060)   |                   			|
|                   			|         ACK                           |
|                   			|(5060)   ------------------>  (5060)   |
|         BYE       			|                                       |
|(5061)   ------------------>  (5060)   |                   			|
|                   			|         BYE                           |
|                   			|(5060)   ------------------>  (5060)   |
|                   			|         200 OK                        |
|                   			|(5060)   <------------------  (5060)   |
|         200 OK    			|                                       |
|(5061)   <------------------  (5060)   |                   			|

Por el momento es todo.

Softphone Java

Buscando un “Softphone” que pudiera utilizar forzosamente en OpenSolaris para realizar pruebas y cosas de un libro de OpenSIPS que ando leyendo, me encuentro con esta aplicación escrita en Java, el cual tiene muy buena presentación aparte de no solo poder usarlo para cuentas SIP si no también para demás cuentas de mensajería instantánea.

sipcommunicator

Aquí esta la pagina para descargarlo: http://sip-communicator.org/

Error al iniciar Solaris en servidor SunFire

En ocasiones cuando hay fallas de corriente y un equipo se enciende y apaga de inmediato pueden presentarse ciertos tipos de errores, cuando es tu propia computadora te lo puedes tomar un poco mas con calma, pero cuando es un servidor y en este caso de nivel algo critico las cosas se pueden tornar un poco mas tensas.

La imagen muestra la pantalla de error al no poder iniciarse el sistema operativo correctamente, y como este equipo administra una seria de equipos ligeros SunRay la actividad de varios usuarios se quedo interrumpida.

error-solaris-server2

Afortunadamente la solución esta en la misma pantalla: svcadm clear system/boot-archive

Después de ejecutar el comando este mismo revisa los servicios y el sistema operativo vuelve a iniciarse correctamente.

Para más información: http://flux.org.uk/howto/solaris/fix_boot_archive

GlassFish y MySQL en OpenSolaris

Siguiendo con el orden de las cosas acerca de ir conociendo un poquito de OpenSolaris, y aprovechando el frió que asolo este fin de semana a la gran Tenochtitlan, decidí agregarle unos cuantos paquetes mas al sistema, entre ellos el servidor de aplicaciones glassfish para crear unas sencillas consultas MySQL mediante jsp, he aquí el procedimiento:

Instalamos el paquete de GlassFish, mediante el gestor de paquetes de OpenSolaris.

zeratul@aiur:~$ pfexec pkg install glassfish-2
zeratul@aiur:~$ pfexec pkg info glassfish-2
                         Nombre: web/glassfish-2
                       Resumen: GlassFish Java EE 5 Application Server
                      Categoría: Web Services/Application and Web Servers
                          Estado: Instalado
     Editor: opensolaris.org
                         Versión: 2.1
                         Versión: 5.11
                  Ramificación: 0.111
Fecha de empaquetado: Mon May 18 20:20:27 2009
                         Tamaño: 81.32 MB
                              FMRI: pkg:/web/glassfish-2@2.1,5.11-0.111:20090518T202027Z

Modificamos la linea que se muestra a continuación, presente en el siguiente archivo: /usr/appserver/config/asenv.conf, donde cambiaremos la ruta desde donde iniciaremos nuestro dominio, esto con el fin de establecer los archivos de configuración con los permisos asociados a nuestro usuario, se muestra la linea original comentada y la linea haciendo referencia a nuestro home.

#AS_DEF_DOMAINS_PATH=”/var/appserver/domains”
AS_DEF_DOMAINS_PATH=”/export/home/zeratul/appserver/domains”

Creamos el dominio con la siguiente instrucción, establecemos una contraseña para el usuario admin y dejamos la contraseña por defecto para el usuario master.

zeratul@aiur:~$ asadmin create-domain --user admin --adminport 4848 --savemasterpassword=true domain1
Please enter the admin password>
Please enter the admin password again>
Please enter the master password [Enter to accept the default]:>
Please enter the master password again [Enter to accept the default]:>
Using port 4848 for Admin.
Using default port 8080 for HTTP Instance.
Using default port 7676 for JMS.
Using default port 3700 for IIOP.
Using default port 8181 for HTTP_SSL.
Using default port 3820 for IIOP_SSL.
Using default port 3920 for IIOP_MUTUALAUTH.
Using default port 8686 for JMX_ADMIN.
Domain being created with profile:developer, as specified by variable AS_ADMIN_PROFILE in configuration file.
------ Using Profile [developer] to create the domain ------
XML processing for profile: Base document [/usr/appserver/lib/install/templates/default-domain.xml.template]. Profile name [developer]. Processing property [domain.xml.style-sheets].

The file in given locale [es_MX] at: [/usr/appserver/lib/install/templates/locales/es_MX/index.html] could not be found. Using default (en_US) index.html instead.
Security Store uses: JKS
Domain domain1 created.

Iniciamos el servicio.

zeratul@aiur:~$ asadmin start-domain domain1
Starting Domain domain1, please wait.
Default Log location is /export/home/zeratul/appserver/domains/domain1/logs/server.log.
Redirecting output to /export/home/zeratul/appserver/domains/domain1/logs/server.log
Domain domain1 failed to startup. Please check the server log for more details.
CLI156 Could not start the domain domain1.

Si se nos presenta el error anterior de que no se puede iniciar el dominio, agregamos el nombre del equipo al archivo hosts en la linea que hace referencia a la dirección de loopback.

zeratul@aiur:~$ cat /etc/hosts
::1 aiur aiur.local localhost loghost
127.0.0.1 aiur.local localhost loghost aiur	;Agregar el nombre del equipo al final de la linea (aiur)

Volvemos a iniciar el dominio donde iniciara correctamente el servicio.

zeratul@aiur:~$ asadmin start-domain domain1
Starting Domain domain1, please wait.
Default Log location is /export/home/zeratul/appserver/domains/domain1/logs/server.log.
Redirecting output to /export/home/zeratul/appserver/domains/domain1/logs/server.log
Domain domain1 is ready to receive client requests. Additional services are being started in background.
Domain [domain1] is running [Sun GlassFish Enterprise Server v2.1 (9.1.1) (build b60e-fcs)] with its configuration and logs at: [/export/home/zeratul/appserver/domains].
Admin Console is available at [http://localhost:4848].
Use the same port [4848] for "asadmin" commands.
User web applications are available at these URLs:
[http://localhost:8080 https://localhost:8181 ].
Following web-contexts are available:
[/web1  /__wstx-services ].
Standard JMX Clients (like JConsole) can connect to JMXServiceURL:
[service:jmx:rmi:///jndi/rmi://aiur.local:8686/jmxrmi] for domain management purposes.
Domain listens on at least following ports for connections:
[8080 8181 4848 3700 3820 3920 8686 ].
Domain does not support application server clusters and other standalone instances.

Nos dirigimos a un navegador web y mediante el puerto 8080, GlassFish nos debe dar su pagina de bienvenida.

glassfish1-1

Ahora necesitamos el controlador de Java para poder conectarnos a bases de datos MySQL, descargamos la ultima versión del siguiente sitio: http://dev.mysql.com/downloads/connector/j/

Necesitamos colocar el archivo .jar que viene en el archivo comprimido dentro de la carpeta ext que se ubica en la siguiente ruta: /export/home/zeratul/appserver/domains/domain1/lib/ext

zeratul@aiur:~/appserver/domains/domain1/lib/ext$ ls
mysql-connector-java-5.1.10-bin.jar

Paramos e iniciamos el servidor glassfish para que tome los cambios.

zeratul@aiur:~$ asadmin stop-domain domain1
zeratul@aiur:~$ asadmin start-domain domain1

Ahora realizaremos un pequeño archivo jsp para realizar una sencilla consulta al servidor MySQL que guarda los registros de llamadas de nuestro Asterisk. De manera rápida crearemos un archivo llamado listar.jsp, dentro de la carpeta: /export/home/zeratul/appserver/domains/domain1/docroot




Ejemplo consulta  JSP

<%
Connection  canal = null;
Statement instruccion=null;
ResultSet tabla= null;
String conexion =  "jdbc:mysql://mysql.demerzel.org:3306/asteriskcdr?user=asterisk&password=mysecret";

//Abrir el enlace
try{
Class.forName("com.mysql.jdbc.Driver").newInstance();
canal =  DriverManager.getConnection(conexion);
instruccion =  canal.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
}
catch(ClassNotFoundException e){ out.println(e);}
catch(SQLException e){ out.println(e);}
try{
tabla =  instruccion.executeQuery("select src, dst, calldate, sec_to_time(billsec), channel, dstchannel from cdr where billsec!=0 and disposition='ANSWERED' and calldate between '2009-12-01 00:00:00' and '2009-12-31 23:59:59' order by calldate, src, dst limit 25");
out.println("");
out.println("");
while(tabla.next()){
out.println("");
}
out.println("
OrigenDestinoFechaDuraciónCanal OrigenCanal Destino
"+tabla.getString(1)+""+tabla.getString(2)+""+tabla.getString(3)+""+tabla.getString(4)+""+tabla.getString(5)+""+tabla.getString(6)+"
"); } catch(Exception e){ out.println(e);} %>

El servidor MySQL no esta en el mismo equipo por lo tanto debemos darle los permisos adecuados al usuario para realizar consultas desde OpenSolaris. Una sentencia sencilla como la siguiente sera suficiente:

GRANT SELECT ON asteriskcdr.* TO asterisk@192.9.200.114 IDENTIFIED BY 'mysecret';

Ingresamos a la dirección web http://midirecciónip:8080/listar.jsp, y se nos debe desplegar el listado de llamadas de las que hicimos consulta.

glassfish2-1

Referencias:
El articulo original de como instalar GlassFish.
http://blogs.sun.com/observatory/en_US/entry/glassfish
De aquí tome el archivo jsp de ejemplo.
http://casidiablo.net/instalacion-de-un-entorno-web-tomcat-jsp-mysql/