OpenSIPS: filtrare le chiamate in ingresso ed uscita

Creato il 08 ottobre 2015 da Michelepinassi @michele_pinassi

Per esigenze di lavoro, alcuni colleghi hanno la necessità di non essere raggiunti telefonicamente dall'esterno della rete telefonica di Ateneo. Essendo però tutti i nostri interni "passanti", ovvero raggiungibili anche attraverso numerazione geografica dall'esterno, le soluzioni potevano essere due: creare uno spazio di numerazione interno non passante, ovvero non raggiungibile dall'esterno oppure trovare il modo di discriminare le chiamate sul numero chiamante.

Scegliere di assegnare ai colleghi una numerazione fittizia avrebbe potuto comportare alcuni problemi in futuro, ad esempio nel caso l'esigenza di non essere raggiungibili venisse meno. Inoltre questo avrebbe causato alcuni inconvenienti per le chiamate dirette verso l'esterno: quale CID assegnare, se l'interno non è geograficamente valido ? Così l'unica strada percorribile era quella di creare un filtro discriminante basato sul numero del chiamante.

Filtrare chiamate in USCITA

Il primo approccio è stato con il modulo " userblacklist" di OpenSIPS, che offre la possibilità per l'utente di impostare delle black e white list discriminanti: uso già tale modulo nella funzionalità di global blacklist per indicare quali numeri sono bloccati (ad es. gli 144) e quali permessi, attraverso la tabella globalblacklist come indicato nella documentazione stessa:

if (!check_blacklist("globalblacklist")))
sl_send_reply("403", "Forbidden");
exit;
}

per le blacklist lato utente, il meccanismo è simile:

if (!check_user_blacklist("user", "domain.com"))
	sl_send_reply("403", "Forbidden");
	exit;
}

ma avendolo già attuato nel mio script di routing, avevo qualche difficoltà ad adattarlo anche per le chiamate in ingresso. Nel dettaglio, vediamo come applicare sia la global che la user black list per le chiamate in uscita:

[...]
# do lookup with method filtering
if (!lookup("location","m")) {
 switch ($retcode) {
  case -1: # Note here: check for global blacklist numbers
    if (!check_blacklist("globalblacklist")) {
     xlog("L_ERR", "Number $rU GLOBAL BLACKLISTED\n");
     sl_send_reply("403", "Forbidden - Call not allowed");
     exit;
    }
    # Check for user blacklist
    $avp(80) = $rU;
    if (!check_user_blacklist("$avp(80)", "$avp(82)")) {
     xlog("L_ERR", "Number $rU BLACKLISTED\n");
     sl_send_reply("403", "Forbidden - User not allowed to call this number");
     exit;
    }
[...]
}

Per chi non ha familiarità con lo script di routing di OpenSIPS, molto velocemente la funzione lookup permette di sapere se l'utenza di destinazione è locale ed on-line (registrata con il server OpenSIPS), locale ma non attualmente registrata oppure non è locale, ritornando uno dei seguenti valori:

Nel pezzo di script riportato, se la funzione restituisce "-1" significa che il numero chiamato è al di fuori del contesto VoIP universitario: verifichiamo pertanto se può essere contattato e poi inoltriamo la richiesta al gateway con l'esterno.

La tabella di blacklist utente, userblacklist, contiene i medesimi campi della global con l'aggiunta della colonna "username" che indica su quale utenza deve essere applicata la restrizione. Per il campo "prefix", che indica materialmente il filtro di blocco, si applica il principio della precedenza sul più "lungo" e tutte le regole che nel campo "whitelist" hanno valore 0 sono bloccate, con valore 1 sono accettate (default).

Ad esempio, se voglio impedire le chiamate sui numeri 05 ad eccezione dei prefissi 0577,0564 e 0575, posso aggiungere una riga con il blocco sul prefisso 05 ed altre con i prefissi esplicitamente ammessi (campo "whitelist" = 1), come nell'immagine a destra.

Con questo meccanismo è possibile un controllo abbastanza capillare dei permessi in uscita ma la mancanza del supporto per le PCRE rende il meccanismo limitato per necessità più particolari, come le chiamate in ingresso dove è necessario anche un controllo sulla lunghezza del numero.

Filtrare chiamate in INGRESSO

Per attuare un filtro sulle chiamate in ingresso ho scoperto il modulo regex che mi permette, attraverso l'uso di un file e delle fantastiche PCRE ( Perl Compatible Regular Expressions), di gestire con sufficiente flessibilità le regole per determinare le chiamate in ingresso, sia globalmente che per singoli utenti.

Nel mio caso ho anche usato la funzione che permette di caricare AVP, variabili Attribute Value Pair, direttamente da tabelle sul database: le AVP sono un "meccanismo" di OpenSIPS per la memorizzazione di variabili di sessione in modo dinamico ed il loro uso è molto semplice:

$avp(nome) = valore

utilizzando la funzione avp_db_load() posso caricare dinamicamente tutte le AVP corrispondenti ad un certo username direttamente dalla tabella usr_preferences del DBMS. Ad esempio, prendendo i dati nell'immagine a destra, vediamo che alcune utenze (colonna "username") hanno l'attributo "incoming" ed il valore "onlylocal". Nello script di routing è gestito così:

#### REGEX module
loadmodule "regex.so"
modparam("regex", "file", "/etc/opensips/groups.lst")
[...]
if(avp_db_load("$ru","$avp(incoming)")) {
 switch($avp(incoming)) {
  case "onlylocal": # Solo chiamate LOCALI
   if(!pcre_match_group("$fU","0")) { # Definiti nel gruppo "0" del file REGEX 
    xlog("L_ERR","403 - Forbidden - Call not allowed from $fU\n");
    sl_send_reply("403","Forbidden");
    exit;
   }
   break;
  case "none": # Blocca tutte le chiamate in ingresso
   xlog("L_ERR","403 - Forbidden - Call not allowed from $fU\n");
   sl_send_reply("403","Forbidden");
   exit;
  case "all": # Abilita tutte le chiamate in ingresso (default)
   break;
 }
}

La variabile $ru ( Request-URI) contiene l'URI del destinatario ( sip:[interno]@[dominio]) mentre la varibile $fU (From-URI) contiene solamente il numero del chiamante: pertanto se l'utenza contattata ha l'AVP " incoming" impostato su " onlylocal" ed il numero telefonico del chiamante non è contenuto all'interno del gruppo " 0" delle espressioni regolari definite nel file /etc/opensips/groups.lst, la richiesta viene rigettata con l'errore 403 - Forbidden.

Ad esempio, se nel file groups.lst ho definiti questi gruppi:

### List of groups used by REGEX module
## Internal numbers
[0]
^5\d{3}$

la verifica del gruppo "0" da parte della funzione pcre_match_group([valore],[gruppo]) restituirà TRUE se [valore] viene rispettato o FALSE altrimenti. In questo specifico caso, restituirà TRUE solamente se il numero del chiamante inizia con 5 ed è di 4 numeri complessivi.

Ovviamente questo è semplicemente il modo che ho studiato per ottenere il risultato desiderato: se conoscete altri sistemi vi invito a condividerli !

Nella foto di copertina, dettaglio di un vecchio permutatore telefonico.