Archivo de la etiqueta: Kamailio

Protegiendo nuestro sistema de VoIP con Kamailio

 

Cuando se gestiona una centralita local, lo normal es no tener ningún tipo de puerto abierto al exterior, dado que las conexiones las realizamos siempre nosotros hacia nuestro proveedor de VoIP. A no ser que realicemos una validación por IP, con lo que deberemos permitir el acceso a nuestro puerto SIP única y exclusivamente a la IP que nos facilite nuestro(s) operador(es).

En el caso de que administremos un sistema más complejo, o simplemente, ofrezcamos servicios de VoIP a terceros, seguramente tendremos otros elementos involucrados en nuestro sistema, como servidores proxy.

En mi último post – guau! hace más de 1 año ya! – escribí sobre Kamailio y, sin despreciar a su ‘hermano’ OpenSIPS, voy a poner algunas configuraciones bastante útiles para protegernos tanto de escaneos como de ataques (fuerza bruta, DoS, …). Para ello vamos a utilizar programación en LUA, que nos permitirá dejar un poco más limpio nuestro fichero de configuración de Kamailio.

 

Evitando los molestos escáner SIP

Normalmente todos los escáner SIP utilizan un User-Agent propio por defecto. De hecho, parece mentira que el escáner más conocido, SIPVicious, esté programado en Python y la gente siga usándolo ‘sin alterar’, es decir, sin cambiar ese agente de usuario tan conocido … hablo de ‘friendly-scanner’. Por tanto, nos sobra con poner un pequeño filtro en nuestro Kamailio que busque los UA más conocidos … algo así:

route[REQINIT] {
..........

if($ua == "friendly-scanner" || $ua == "sundayddr" || $ua == "sip-scan" || $ua == "iWar" || $ua == "sipsak") {
   xlog("L_ALERT", "Attack attempt! IP:$si:$sp - R:$ruri - F:$fu - T:$tu - UA:$ua - $rm\n");
   drop();
}

..........
}

 

Lo que estamos haciendo es un drop del paquete cuando detectemos un User_agent que concuerde con un escáner SIP conocido.

 

Módulo pike + htable de Kamailio

Los módulos pikehtable de Kamailio son más que conocidos y nos permiten bloquear intentos de registro erróneos. Se puede ver toda la información en la documentación de Kamailio:

http://kamailio.org/docs/modules/stable/modules/pike.html

http://kamailio.org/docs/modules/stable/modules/htable.html

Por ejemplo, podemos definir cómo queremos que se comporte en la definición de parámetros, donde size es el número de intentos y autoexpire indica el tiempo tras el cual se libera el bloqueo:

modparam("htable", "htable", "wrongpass=>size=8;autoexpire=900")
modparam("htable", "htable", "ipban=>size=8;autoexpire=300")

 

En la ruta de autenticación comprobaremos el número de intentos erróneos y, si es superior al permitido, lo bloquearemos:

route[AUTH] {
..........

        if (is_method("REGISTER|INVITE") || from_uri==myself)
        {
                # User reached auth_count top
                if($sht(wrongpass=>$au::auth_count)==$sel(cfg_get.auth.max{s.int}))
                {
                        $var(exp) = $Ts - $sel(cfg_get.auth.bantime{s.int});

                        if ($sht(wrongpass=>$au::last_auth) > $var(exp))
                        {
                                # no more auth checks
                                xdbg("username $au $rm blocked from $proto:$si:$sp\n");
                                # we also can send a fake 200 OK reply
                                # sl_send_reply("200","OK");
                                exit;
                        } else {
                                # bantime seg reached... clean count
                                $sht(wrongpass=>$au::auth_count) = 0;
                        }
                }

                # authenticate requests
                if (!radius_www_authorize("mysip.server.com")) {
                        switch ($rc) {
                        case -7:
                           send_reply("500", "Server Internal Error");
                           exit;
                        case -2:
                                if ($sht(wrongpass=>$au::auth_count) == $null) {
                                        $sht(wrongpass=>$au::auth_count) = 0;
                                }

                                $sht(wrongpass=>$au::auth_count) = $sht(wrongpass=>$au::auth_count) + 1;

                                if ($sht(wrongpass=>$au::auth_count) == $sel(cfg_get.auth.max{s.int})) {
                                        xlog("L_ALERT","Auth failed $sel(cfg_get.auth.max) times - user:$au from $proto:$si:$sp\n");
                                }

                                $sht(wrongpass=>$au::last_auth) = $Ts;
                                break;
                        case -1:
                           send_reply("400", "Bad Request");
                           exit;
                        default:
                        };
                        if (defined($avp(digest_challenge)) &&
                                ($avp(digest_challenge) != "")) {
                                append_to_reply("$avp(digest_challenge)");
                        };
                        send_reply("401", "Unauthorized");
                        exit;
                }
                # clean
                if ($sht(wrongpass=>$au::auth_count) != $null) {
                        $sht(wrongpass=>$au::auth_count) = 0;
                }
        }

..........
}

 

Creando listas negras

A parte de estos módulos que nos ofrece Kamailio, podemos programar todo lo que se nos ocurra, gracias al enorme potencial de Kamailio. Por ejemplo, podemos crear listas negras y blancas de usuarios o de IPs para permitir o bloquear a determinadas direcciones IP o usuarios.

Por ejemplo, sirva como base la siguiente tabla de MySQL para crear listas negras de direcciones IP:

CREATE TABLE IF NOT EXISTS `black_list` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(32) COLLATE utf8_spanish_ci NOT NULL,
`detail` varchar(255) COLLATE utf8_spanish_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci AUTO_INCREMENT=42 ;

Podemos utilizar los campos:

– ip: para bloquear una dirección IP
– detail: para añadir alguna observación

Desde Kamailio comprobaremos si está en la lista negra, en cuyo caso bloquearemos la petición:

route[REQINIT] {
..........

        if(src_ip!=myself)
        {
                # check if the IP is blacklisted
                lua_run("check_blacklist", "$si");
                if ($avp(s:wb_status) == "black") {
                       xlog("L_ALERT","Received $rm from a blacklisted $avp(s:wb_type)! $fU (IP:$si) - Sending Drop ...\n");
                       $sht(ipban=>$si) = 1;
                       drop();
                }
         }

..........
}

Y usando LUA programaremos la función check_blacklist

-- check blacklist
function check_blacklist(ip)
        local con, cur, row
        con = connect_ro('openser')
        cur = assert (con:execute("SELECT id FROM black_list WHERE ip='" .. ip .. "'))
        row = cur:fetch({}, "a")
        cur:close()
        con:close()
        if row then
                sr.pv.sets('$avp(wb_status)', 'black')
        else
                sr.pv.sets('$avp(wb_status)', '')
        end

        return
end

 

Al igual que bloqueamos un paquete que provenga de una determinada dirección IP, sería muy sencillo bloquear ciertos usuarios, agentes de usuario, etc.

 

Filtrado por países

Del mismo modo que bloqueamos direcciones IP, podemos realizar una comprobación del país que realiza las peticiones. Por ejemplo:

CREATE TABLE IF NOT EXISTS `black_list_country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`country` varchar(5) COLLATE utf8_spanish_ci NOT NULL,
`detail` varchar(255) COLLATE utf8_spanish_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci AUTO_INCREMENT=42 ;

Podemos utilizar los campos:

– country: código de país a bloquear
– detail: para añadir alguna observación

Por otro lado:

CREATE TABLE IF NOT EXISTS `ipcountry` (
  `ipstart` varchar(20) NOT NULL,
  `ipend` varchar(20) NOT NULL,
  `countrycode` varchar(5) NOT NULL,
  `ipfrom` bigint(11) NOT NULL,
  `ipto` bigint(11) NOT NULL,
  KEY `ipstart` (`ipstart`,`ipend`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Donde:

– ipstart e ipend: rangos de IPs (ejemplo: 2.20.179.0 – 2.20.179.255)
– countrycode: código del país (ejemplo: ES)
– ipfrom e ipto: IPs en formato decimal (ejemplo: 34910976 – 34911231)

Podemos descargar la base de datos de IPs de aquí: http://db-ip.com/db/download/country

Y el motivo por el que meto en la base de datos las IPs en dos formatos es por temas de optimización, para no tener que calcularlas en cada consulta. Para ello, tras importar la base de datos deberemos calcular las direcciones IP en su formato decimal:

mysql> update `ipcountry` set ipfrom = INET_ATON(ipstart);
mysql> update `ipcountry` set ipto = INET_ATON(ipend);

Y desde Kamailio, para filtrar los países bloqueados, será de forma similar a lo visto antes. Comprobamos si el país está bloqueado y hacemos un drop a la vez que bloqueamos la IP:

route[REQINIT] {
..........

      lua_run("get_countrycode", "$si");
      lua_run("check_country_blacklist", "$avp(s:countryCode)");
      if ($avp(s:wb_status) == "black") {
              xlog("L_ALERT","Received $rm from a blacklisted country ($avp(s:countryCode))! $fU (IP:$si) - Sending Drop ...\n");
             $sht(ipban=>$si) = 1;
             drop();
       }

..........
}

 

En el LUA tendremos:

-- get country code from IP address
function get_countrycode(ipaddr)
        local con, cur, row
        con = connect_ro('openser')
        cur = assert (con:execute("SELECT countrycode FROM ipcountry WHERE INET_ATON('" .. ipaddr .. "') BETWEEN ipfrom AND ipto LIMIT 1"))
        row = cur:fetch({}, "a")
        cur:close()
        con:close()
        if row then
                sr.pv.sets('$avp(countryCode)', row.countrycode)
        else
                sr.pv.sets('$avp(countryCode)', '')
        end

        return
end

-- check countries blacklisted
function check_country_blacklist(country)
        local con, cur, row
        con = connect_ro('openser')
        cur = assert (con:execute("SELECT id FROM black_list_country WHERE country='" .. country .. "'"))
        row = cur:fetch({}, "a")
        cur:close()
        con:close()
        if row then
                sr.pv.sets('$avp(wb_status)', 'black')
        else
                sr.pv.sets('$avp(wb_status)', '')
        end

        return
end

 

Si lo ejecutamos bloqueando IPs españolas obtendremos algo así:

Jul 30 20:44:51 sip /usr/sbin/kamailio[1672]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user1 (IP:62.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:52 sip /usr/sbin/kamailio[1672]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user2 (IP:62.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:53 sip /usr/sbin/kamailio[1674]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user3 (IP:83.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:53 sip /usr/sbin/kamailio[1672]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user4 (IP:83.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:53 sip /usr/sbin/kamailio[1693]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user5 (IP:62.xx.xx.xx) - Sending Drop ...

 

De lectura obligatoria:
http://www.kamailio.org/wiki/tutorials/security/kamailio-security

 

Creando un honeypot con Kamailio

Kamailio es un proxy SIP derivado de un proyecto anterior llamado OpenSER. A parte de ser Open Source, es una magnífica herramienta, indispensable si deseas montar un sistema de VoIP medianamente grande, es decir, llegando más allá de las típicas centralitas Asterisk, FreeSwitch o similares que nos permiten gestionar una oficina con X extensiones.

Aprovecho para mandar un saludo a mi compañero Victor Seva (@linuxmaniac) que desde hace poco pertenece al equipo de desarrolladores de Kamailio. 😉

Como comentaba, un proxy SIP nos permite realizar muchísimas cosas, como validar usuarios, reenviar el tráfico a servidores SIP, gestionar llamadas, etc. En este caso lo vamos a utilizar con las mínimas opciones. Simplemente le permitiremos recibir y emitir llamadas, pero con alguna que otra peculiaridad. Nuestra finalidad será la de controlar los escanéos que nos hagan e interceptar todas las llamadas que intenten realizarse a través del sistema, es decir, colocar un honeypot para cazar listillos.

Evidentemente para entender todo esto hay que tener unos mínimos conocimientos de SIP. Si quieres instalar un Kamailio, puedes consultar la documentación aquí: http://www.kamailio.org/wiki/

En primer lugar vamos a editar el fichero kamailio.cfg y vamos a añadir a la ruta REQINIT lo siguiente:


if($ua == "friendly-scanner" || $ua == "sundayddr" || $ua == "sip-scan" || $ua == "iWar" || $ua == "sipsak") {
 xlog("L_ALERT", "Attack attempt! IP:$si:$sp - R:$ruri - F:$fu - T:$tu - UA:$ua - $rm\n");
}

Lo que conseguimos con esto es que nos alerte ante un escaneo usando los programas típicos. Por ejemplo, con SIPVicious:


$ python svmap.py 192.168.2.18

| SIP Device | User Agent | Fingerprint |
---------------------------------------------------------------------
| 192.168.2.18:5060 | kamailio (4.0.1 (x86_64/linux)) | disabled |

En el log del sistema (/var/log/kamailio) vemos esto:


ALERT: <script>: Attack attempt! IP:192.168.2.10:5060 - R:sip:100@192.168.2.18 -
 F:sip:100@1.1.1.1 - T:sip:100@1.1.1.1 - UA:friendly-scanner - OPTIONS

De esta forma nos enteraremos enseguida si alguien nos realiza un escaneo con svmap. De todas formas, siempre viene bien ocultar nuestra versión de kamailio. así que en el fichero de configuración añadiremos:


####### Global Parameters #########
user_agent_header="User-Agent: kamailio 1.0"
server_header="User-Agent: kamailio 1.0"

Y si repetimos la búsqueda:


$ python svmap.py 192.168.2.18

| SIP Device        | User Agent   | Fingerprint |
--------------------------------------------------
| 192.168.2.18:5060 | kamailio 1.0 | disabled    |

Una vez que somos capaces de detectar los escaneos, vamos a jugar un poco con el atacante. Lo primero de todo, le vamos a permitir registrarse en nuestro sistema con cualquier usuario y con cualquier contraseña. Para ello, en la ruta REQINIT añadiremos:


if (is_method("REGISTER")) {
	xlog("L_INFO", "Get Register for user $fU (IP: $si)\n");

	# aceptamos todas las peticiones de registro sin verificar la contraseña
	sl_send_reply("200","OK");
}

Lo que hacemos con esto es permitir el registro de cualquier usuario con cualquier contraseña. Para ello responderemos con un ‘200 OK’, permitiéndoles siempre registrarse.

Si pasamos svcrack podemos ver que siempre ‘descubre’ la contraseña de cualquier usuario:


$ python svcrack.py -u100 -r1-100 -z4 192.168.2.18

| Extension | Password      |
-----------------------------
| 100       | [no password] |

$ python svcrack.py -u324324243 -r1-100 -z4 192.168.2.18

| Extension | Password      |
-----------------------------
| 324324243 | [no password] |

El motivo por el que dice que no tiene contraseña es porque SIPVicious lo primero que prueba ante un ataque por fuerza bruta, es la contraseña vacía.

Si intentamos registrar un teléfono, veremos que se conecta sin problemas. Y en el log aparecerá algo así:


INFO: <script>: Get Register for user pepelux (IP: 192.168.2.16)
INFO: <script>: Get Register for user 200 (IP: 192.168.2.17)

Tras esto, vamos a registrarnos con el usuario 100 (por ejemplo) y lo que haremos será desviar todas las llamadas hacia nosotros. En la ruta REQINIT añadimos:


if (is_method("INVITE")) {
	xlog("L_INFO", "$fU is trying to call to $rU\n");
	$rU = "100"; # siempre nos llamará a nosotros
	xlog("L_INFO", "Sending call to $rU\n");
}

Y cuando se intente realizar una llamada a cualquier número nos sonará a nosotros:


INFO: <script>: pepelux (with IP:192.168.2.16) is trying to call to 666666666
INFO: <script>: Sending call to 100

INFO: <script>: 200 (with IP:192.168.2.17) is trying to call to 123456
INFO: <script>: Sending call to 100

¿Qué tal si en lugar de desviar la llamada a la extensión 100 la desviamos al número 091? 🙂

Y el fichero completo (kamailio.cfg) que he usado para las pruebas:

 


#!KAMAILIO
####### Include Local Config If Exists #########
import_file "kamailio-local.cfg"

####### Defined Values #########
#!define FLT_ACC 1
#!define FLT_ACCMISSED 2
#!define FLT_ACCFAILED 3
#!define FLT_NATS 5
#!define FLB_NATB 6
#!define FLB_NATSIPPING 7

####### Global Parameters #########
user_agent_header="User-Agent: kamailio 1.0"
server_header="User-Agent: kamailio 1.0"

### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR
#!ifdef WITH_DEBUG
debug=4
log_stderror=yes
#!else
debug=2
log_stderror=no
#!endif

memdbg=5
memlog=5
log_facility=LOG_LOCAL0
fork=yes
children=4
port=5060
tcp_connection_lifetime=3605

####### Modules Section ########
# set paths to location of modules (to sources or installation folders)
#!ifdef WITH_SRCPATH
mpath="modules_k:modules"
#!else
mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib64/kamailio/modules/"
#!endif

#!ifdef WITH_MYSQL
loadmodule "db_mysql.so"
#!endif

loadmodule "mi_fifo.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "mi_rpc.so"
loadmodule "acc.so"

# ----------------- setting module-specific parameters ---------------
# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")

# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)

# ----- rr params -----
# add value to ;lr param to cope with most of the UAs
modparam("rr", "enable_full_lr", 1)
# do not append from tag to the RR (no need for this script)
modparam("rr", "append_fromtag", 0)

# ----- registrar params -----
modparam("registrar", "method_filtering", 1)
/* uncomment the next line to disable parallel forking via location */
# modparam("registrar", "append_branches", 0)
/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)
# max value for expires of registrations
modparam("registrar", "max_expires", 3600)
# set it to 1 to enable GRUU
modparam("registrar", "gruu_enabled", 0)

# ----- acc params -----
/* what special events should be accounted ? */
modparam("acc", "early_media", 0)
modparam("acc", "report_ack", 0)
modparam("acc", "report_cancels", 0)
/* by default ww do not adjust the direct of the sequential requests.
   if you enable this parameter, be sure the enable "append_fromtag"
   in "rr" module */
modparam("acc", "detect_direction", 0)
/* account triggers (flags) */
modparam("acc", "log_flag", FLT_ACC)
modparam("acc", "log_missed_flag", FLT_ACCMISSED)
modparam("acc", "log_extra", 
	"src_user=$fU;src_domain=$fd;src_ip=$si;"
	"dst_ouser=$tU;dst_user=$rU;dst_domain=$rd")
modparam("acc", "failed_transaction_flag", FLT_ACCFAILED)
/* enhanced DB accounting */

####### Routing Logic ########
# Main SIP request routing logic
# - processing of any incoming SIP request starts with this route
# - note: this is the same as route { ... }
request_route {
	# per request initial checks
	route(REQINIT);

	# NAT detection
	route(NATDETECT);

	# CANCEL processing
	if (is_method("CANCEL"))
	{
		if (t_check_trans()) {
			route(RELAY);
		}
		exit;
	}

	# handle requests within SIP dialogs
	route(WITHINDLG);

	### only initial requests (no To tag)
	t_check_trans();

	# record routing for dialog forming requests (in case they are routed)
	# - remove preloaded route headers
	remove_hf("Route");
	if (is_method("INVITE|SUBSCRIBE"))
		record_route();

	# dispatch requests to foreign domains
	route(SIPOUT);

	# handle registrations
	route(REGISTRAR);

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

	# user location service
	route(LOCATION);
}

route[RELAY] {
	# enable additional event routes for forwarded requests
	# - serial forking, RTP relaying handling, a.s.o.
	if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {
		if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH");
	}
	if (is_method("INVITE|SUBSCRIBE|UPDATE")) {
		if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY");
	}
	if (is_method("INVITE")) {
		if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
	}

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

# Per SIP request initial checks
route[REQINIT] {
        # Si detectamos el User-Agent de un escáner conocido, mostramos una alerta
        if($ua == "friendly-scanner" || $ua == "sundayddr" || $ua == "sip-scan" || $ua == "iWar" || $ua == "sipsak") {
                xlog("L_ALERT", "Attack attempt! IP:$si:$sp - R:$ruri - F:$fu - T:$tu - UA:$ua - $rm\n");
        }

        # Al recibir un intento de registro, devolvemos siempre un OK
	if (is_method("REGISTER")) {
		xlog("L_INFO", "Get Register for user $fU (IP: $si)\n");

		if ($fU != "100")
			sl_send_reply("200","OK");
	}

        # Al recibir un intento de llamada, modificamos el destino para que siempre llame a la extensión 100
	if (is_method("INVITE")) {
		xlog("L_INFO", "$fU (with IP:$si) is trying to call to $rU\n");
		$rU = "100";
		xlog("L_INFO", "Sending call to $rU\n");
	}

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

	if(!sanity_check("1511", "7"))
	{
		xlog("Malformed SIP message from $si:$sp\n");
		exit;
	}
}

# Handle requests within SIP dialogs
route[WITHINDLG] {
	if (has_totag()) {
		# sequential request withing a dialog should
		# take the path determined by record-routing
		if (loose_route()) {
			route(DLGURI);
			if (is_method("BYE")) {
				setflag(FLT_ACC); # do accounting ...
				setflag(FLT_ACCFAILED); # ... even if the transaction fails
			}
			else if ( is_method("ACK") ) {
				# ACK is forwarded statelessy
				route(NATMANAGE);
			}
			else if ( is_method("NOTIFY") ) {
				# Add Record-Route for in-dialog NOTIFY as per RFC 6665.
				record_route();
			}
			route(RELAY);
		} else {
			if (is_method("SUBSCRIBE") && uri == myself) {
				# in-dialog subscribe requests
				exit;
			}
			if ( is_method("ACK") ) {
				if ( t_check_trans() ) {
					# no loose-route, but stateful ACK;
					# must be an ACK after a 487
					# or e.g. 404 from upstream server
					route(RELAY);
					exit;
				} else {
					# ACK without matching transaction ... ignore and discard
					exit;
				}
			}
			sl_send_reply("404","Not here");
		}
		exit;
	}
}

# Handle SIP registrations
route[REGISTRAR] {
	if (is_method("REGISTER"))
	{
		if(isflagset(FLT_NATS))
		{
			setbflag(FLB_NATB);
			# uncomment next line to do SIP NAT pinging 
			## setbflag(FLB_NATSIPPING);
		}
		if (!save("location"))
			sl_reply_error();

		exit;
	}
}

# USER location service
route[LOCATION] {
	$avp(oexten) = $rU;
	if (!lookup("location")) {
		$var(rc) = $rc;
		t_newtran();
		switch ($var(rc)) {
			case -1:
			case -3:
				send_reply("404", "Not Found");
				exit;
			case -2:
				send_reply("405", "Method Not Allowed");
				exit;
		}
	}

	# when routing via usrloc, log the missed calls also
	if (is_method("INVITE"))
	{
		setflag(FLT_ACCMISSED);
	}

	route(RELAY);
	exit;
}

# Caller NAT detection route
route[NATDETECT] {
#!ifdef WITH_NAT
	force_rport();
	if (nat_uac_test("19")) {
		if (is_method("REGISTER")) {
			fix_nated_register();
		} else {
			add_contact_alias();
		}
		setflag(FLT_NATS);
	}
#!endif
	return;
}

# RTPProxy control
route[NATMANAGE] {
#!ifdef WITH_NAT
	if (is_request()) {
		if(has_totag()) {
			if(check_route_param("nat=yes")) {
				setbflag(FLB_NATB);
			}
		}
	}
	if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB)))
		return;

	rtpproxy_manage();

	if (is_request()) {
		if (!has_totag()) {
			add_rr_param(";nat=yes");
		}
	}
	if (is_reply()) {
		if(isbflagset(FLB_NATB)) {
			add_contact_alias();
		}
	}
#!endif
	return;
}

# URI update for dialog requests
route[DLGURI] {
#!ifdef WITH_NAT
	if(!isdsturiset()) {
		handle_ruri_alias();
	}
#!endif
	return;
}

# Routing to foreign domains
route[SIPOUT] {
	if (!uri==myself)
	{
		append_hf("P-hint: outbound\r\n");
		route(RELAY);
	}
}

# manage outgoing branches
branch_route[MANAGE_BRANCH] {
	xdbg("new branch [$T_branch_idx] to $ru\n");
	route(NATMANAGE);
}

# manage incoming replies
onreply_route[MANAGE_REPLY] {
	xdbg("incoming reply\n");
	if(status=~"[12][0-9][0-9]")
		route(NATMANAGE);
}

# manage failure routing cases
failure_route[MANAGE_FAILURE] {
	route(NATMANAGE);

	if (t_is_canceled()) {
		exit;
	}
}

 

Saludos