Configurare Kamailio con rtpengine per gestire traffico RTP dietro NAT

In uno scenario di laboratorio, quindi ideale direi, di solito tutti gli elementi coinvolti in una chiamata VoIP sono sulla stessa subnet e questo vuol dire che i flussi media RTP transitano direttamente tra chiamante e chiamato. Questo però non accade mai nel mondo reale, dove invece lo scenario è molto più complesso e sia i messaggi di segnalazione SIP sia i flussi media devono transitare attraverso firewall e NAT.
In generale possiamo pensare ad uno scenario come quello illustrato nella figura seguente, dove il chiamante è localizzato esternamente alla nostra infrastruttura ed il chiamato si trova su una rete locale dietro un SIP Proxy, che a sua volta è dietro un firewall.

Real SIP Call scenario

Inoltre il SIP Proxy potrebbe avere una singola interfaccia di rete localizzata sulla LAN, oppure potrebbe essere configurato in multihomed con un IP pubblico esposto verso Internet ed un IP privato locale alla LAN. In entrambi i casi, dopo le opportune configurazioni da applicare al firewall, possiamo sfruttare le funzionalità di Kamailio configurato con rtpengine.

Configurazione

Prima di procedere con la configurazione di Kamailio è necessario:

  1. Aver già installato e configurato rtpengine sul server in uso (trovate qui tutti i dettagli).
  2. Prevedere le necessarie configurazioni da applicare al firewall (se presente) far transitare pacchetti da Internet a Kamailio e viceversa.

Nel nostro caso andremo a configurare Kamailio per ricevere una chiamata in arrivo da Internet e ruotarla ad un server interno alla rete locale. In questo scenario Kamailio gestirà due leg di chiamata, uno tra chiamante ed interfaccia pubblica ed uno tra interfaccia privata e destinatario, sia per il traffico SIP che per il flusso media RTP.

Per attivare la gestione del media stream tramite rtpengine è necessario caricare il modulo rtpengine.so con i relativi parametri di configurazione (https://www.kamailio.org/docs/modules/5.5.x/modules/rtpengine.html).

	#!ifdef WITH_NAT
	loadmodule "nathelper.so"
	#!ifdef WITH_RTPENGINE
	loadmodule "rtpengine.so"
	#!else
	loadmodule "rtpproxy.so"
	#!endif
	#!endif
	...
	...
	#!ifdef WITH_NAT
	#!ifdef WITH_RTPENGINE
	# ----- rtpengine params -----
	modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
	#!else
	# ----- rtpproxy params -----
	modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722")
	#!endif

Quindi abilitiamo il caricamento dei moduli nathelper.so e rtpengine.so tramite define in testa al file di configurazione (la direttiva define permette di controllare in stile C quali parti di configurazione devono essere eseguite).

	#!KAMAILIO
	#
	# Global define to activate specific features
	#
	// Enable Debug
	##!define WITH_DEBUG
	 // Enable NAT
	#!define WITH_NAT
	// Enable RTPENGINE
	#!define WITH_RTPENGINE
	...

Riavviamo Kamailio e se tutto è configurato correttamente, eseguendo il comando kamcmd rtpengine.show all avremo un output di questo tipo:

	{
		 url: udp:127.0.0.1:2223
		 set: 0
		 index: 0
		 weight: 1
		 disabled: 0
		 recheck_ticks: 0
	}

Gestione media con rtpengine

Come detto sopra, in questo scenario Kamailio non si comporta da semplice SIP Proxy, ma sarà un vero e proprio RTP relay server tra chiamante e chiamato e per fare questo andrà ad effettuare il rewrite del SDP inserendo il suo IP (pubblico o privato a seconda del leg di chiamata) sul connection information. Quello che dobbiamo fare è quindi attivare il processing tramite rtpengine del INVITE ricevuto e del 200OK inviato, chiamando la funzione rtpengine_manage().

Entriamo nel dettaglio della configurazione

Questa è la mia configurazione di rtpengine (999.999.999.999 è l’IP pubblico):

	OPTIONS="--interface=pub/192.168.0.42!999.999.999.999 --interface=priv/192.168.0.42 -n 127.0.0.1:2223 -m 23000 -M 23100 -L 6 --log-facility=local1 --table=8 --delete-delay=0 --timeout=60 --silent-timeout=600 --final-timeout=7200 –offer-timeout=60 --num-threads=12 --tos=184 –no-fallback"

Kamailio effettua l’SDP rewrite del INVITE nella route[RELAY], potendo anche distinguere tra direzione inbound (interface pub -> priv) e direzione outbound (interface priv -> pub):

	route[RELAY] {
		...
		...
		if (has_body("application/sdp")) {
			if ($rd == "outbound.direction") {
				rtpengine_manage("direction=priv direction=pub");
			}
			else {
				rtpengine_manage("direction=pub direction=priv");
			}
		}
		...
		...

Ed effettua l’SDP rewrite del 200OK nella onreply_route[MANAGE_REPLY]:

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

		if (has_body("application/sdp")) {
			rtpengine_manage();
		}
	}

Inoltre, per una corretta gestione del NAT configuriamo Kamailio in listen sull’interfaccia privata, ma gli diciamo di annunciare l’IP pubblico:

	listen=udp:PRIVATE_IP4_ADDR:SIP_PORT advertise PUBLICIP4_ADDR:SIP_PORT

dove PRIVATE_IP4_ADDR:SIP_PORT è l’IP privato, e PUBLICIP4_ADDR:SIP_PORT e il pubblico.

Kamailio all’opera con rtpengine

Nel mio caso il server con installati Kamailio ed rtpengine è il 192.168.0.42 mentre 10.100.163.4 è il called, e come appare evidente dal tracciato sono attivi due flussi media RTP bidirezionali, terminati proprio da Kamailio che ne sta facendo il relay.

kamailio-rtpengine

Andando ulteriormente nel dettaglio, possiamo vedere come Kamailio (rtpengine) vada ad effettuare il rewrite sul SDP del INVITE e del 200OK. Nelle immagini seguenti ci sono rispettivamente l’INVITE ricevuto con IP pubblico sul connection information e l’invite inoltrato internamente con l’IP locale di Kamailio.

kamailio-rtpengine

kamailio-rtpengine

Lo stesso avviene con il 200OK di risposta, il messaggio in arrivo dal called contiene l’IP locale, mentre il messaggio di uscita verso il caller contiene l’IP pubblico di Kamailio.

kamailio-rtpengine

kamailio-rtpengine