2009-05-30 00:14:57 +00:00
/*
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
2014-02-05 21:02:28 +00:00
* Copyright ( C ) 2005 - 2014 , Anthony Minessale II < anthm @ freeswitch . org >
2009-05-30 00:14:57 +00:00
*
* Version : MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS " basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II < anthm @ freeswitch . org >
* Portions created by the Initial Developer are Copyright ( C )
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
*
* Anthony Minessale II < anthm @ freeswitch . org >
* Brian K . West < brian @ freeswitch . org >
2009-06-30 18:59:05 +00:00
* Rupa Schomaker < rupa @ rupa . com >
2009-05-30 00:14:57 +00:00
*
*
* switch_nat . c NAT Traversal via NAT - PMP or uPNP
*
*/
# include <switch.h>
# include "../libs/miniupnpc/miniwget.h"
# include "../libs/miniupnpc/miniupnpc.h"
# include "../libs/miniupnpc/upnpcommands.h"
# include "../libs/miniupnpc/upnperrors.h"
# include "../libs/libnatpmp/natpmp.h"
2009-06-30 18:59:05 +00:00
# define MULTICAST_BUFFSIZE 65536
2009-06-30 20:11:22 +00:00
# define IP_LEN 16
2010-06-21 00:21:42 +00:00
# define NAT_REFRESH_INTERVAL 900
2009-06-30 18:59:05 +00:00
2009-05-30 00:14:57 +00:00
typedef struct {
switch_nat_type_t nat_type ;
2011-02-02 21:43:26 +00:00
char nat_type_str [ 5 ] ;
2009-05-30 00:14:57 +00:00
struct UPNPUrls urls ;
struct IGDdatas data ;
2009-06-30 18:59:05 +00:00
char * descURL ;
2009-06-30 20:11:22 +00:00
char pub_addr [ IP_LEN ] ;
char pvt_addr [ IP_LEN ] ;
2011-04-01 17:30:24 +00:00
switch_bool_t mapping ;
2009-05-30 00:14:57 +00:00
} nat_globals_t ;
static nat_globals_t nat_globals ;
2009-06-30 18:59:05 +00:00
typedef struct {
switch_memory_pool_t * pool ;
int running ;
switch_sockaddr_t * maddress ;
switch_socket_t * msocket ;
} nat_globals_perm_t ;
static nat_globals_perm_t nat_globals_perm ;
static switch_bool_t first_init = SWITCH_TRUE ;
2010-01-12 00:40:43 +00:00
static switch_bool_t initialized = SWITCH_FALSE ;
2009-06-30 18:59:05 +00:00
static switch_status_t get_upnp_pubaddr ( char * pub_addr )
{
2010-02-06 03:38:24 +00:00
if ( UPNP_GetExternalIPAddress ( nat_globals . urls . controlURL , nat_globals . data . servicetype , pub_addr ) = = UPNPCOMMAND_SUCCESS ) {
2009-10-23 16:03:42 +00:00
if ( ! strcmp ( pub_addr , " 0.0.0.0 " ) | | zstr_buf ( pub_addr ) ) {
2010-02-06 03:38:24 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
" uPNP Device (url: %s) returned an invalid external address of '%s'. Disabling uPNP \n " , nat_globals . urls . controlURL ,
pub_addr ) ;
2009-06-30 18:59:05 +00:00
return SWITCH_STATUS_GENERR ;
}
2009-07-01 03:17:58 +00:00
} else {
return SWITCH_STATUS_GENERR ;
2009-06-30 18:59:05 +00:00
}
return SWITCH_STATUS_SUCCESS ;
}
2010-02-06 03:38:24 +00:00
static int init_upnp ( void )
2009-05-30 00:14:57 +00:00
{
2009-05-30 10:24:38 +00:00
struct UPNPDev * devlist ;
2010-02-11 08:33:03 +00:00
struct UPNPDev * dev = NULL ;
struct UPNPDev * trydev = NULL ;
2010-02-06 03:38:24 +00:00
char * descXML ;
2009-05-30 00:14:57 +00:00
int descXMLsize = 0 ;
2011-12-15 19:02:07 +00:00
const char * minissdpdpath = switch_core_get_variable ( " local_ip_v4 " ) ;
2009-05-30 00:14:57 +00:00
memset ( & nat_globals . urls , 0 , sizeof ( struct UPNPUrls ) ) ;
memset ( & nat_globals . data , 0 , sizeof ( struct IGDdatas ) ) ;
2011-12-15 19:02:07 +00:00
2014-06-24 19:13:10 +00:00
devlist = upnpDiscover ( 3000 , minissdpdpath , minissdpdpath , 0 ) ;
2009-05-30 00:14:57 +00:00
if ( devlist ) {
dev = devlist ;
while ( dev ) {
2010-02-06 03:38:24 +00:00
if ( strstr ( dev - > st , " InternetGatewayDevice " ) ) {
2009-05-30 00:14:57 +00:00
break ;
2009-05-30 10:01:21 +00:00
}
2010-02-11 08:33:03 +00:00
if ( ! trydev & & ! switch_stristr ( " printer " , dev - > descURL ) ) {
trydev = dev ;
}
2009-05-30 00:14:57 +00:00
dev = dev - > pNext ;
}
2010-02-06 03:38:24 +00:00
2010-02-11 08:33:03 +00:00
}
if ( ! dev & & trydev ) {
dev = trydev ; /* defaulting to first device */
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " No InternetGatewayDevice, using first entry as default (%s). \n " , dev - > descURL ) ;
} else if ( devlist & & ! dev & & ! trydev ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " No InternetGatewayDevice found and I am NOT going to try your printer because printers should not route to the internet, that would be DAFT \n " ) ;
}
if ( dev ) {
2009-05-30 00:14:57 +00:00
descXML = miniwget ( dev - > descURL , & descXMLsize ) ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
nat_globals . descURL = strdup ( dev - > descURL ) ;
2010-02-06 03:38:24 +00:00
2009-05-30 00:14:57 +00:00
if ( descXML ) {
2010-02-06 03:38:24 +00:00
parserootdesc ( descXML , descXMLsize , & nat_globals . data ) ;
free ( descXML ) ;
descXML = 0 ;
GetUPNPUrls ( & nat_globals . urls , & nat_globals . data , dev - > descURL ) ;
2010-01-07 16:52:01 +00:00
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Unable to retrieve device description XML (%s). \n " , dev - > descURL ) ;
2009-05-30 00:14:57 +00:00
}
freeUPNPDevlist ( devlist ) ;
}
2009-06-30 18:59:05 +00:00
if ( get_upnp_pubaddr ( nat_globals . pub_addr ) = = SWITCH_STATUS_SUCCESS ) {
2009-05-30 00:14:57 +00:00
nat_globals . nat_type = SWITCH_NAT_TYPE_UPNP ;
return 0 ;
}
return - 2 ;
}
2009-06-30 18:59:05 +00:00
static int get_pmp_pubaddr ( char * pub_addr )
2009-05-30 00:14:57 +00:00
{
2009-06-02 22:46:38 +00:00
int r = 0 , i = 0 , max = 5 ;
2009-05-30 00:14:57 +00:00
natpmpresp_t response ;
char * pubaddr = NULL ;
fd_set fds ;
2009-06-05 15:01:54 +00:00
natpmp_t natpmp ;
2009-07-30 16:20:47 +00:00
const char * err = NULL ;
2010-02-06 03:38:24 +00:00
2009-07-30 16:20:47 +00:00
if ( ( r = initnatpmp ( & natpmp ) ) < 0 ) {
err = " init failed " ;
goto end ;
}
2009-06-02 22:46:38 +00:00
2009-07-30 16:20:47 +00:00
if ( ( r = sendpublicaddressrequest ( & natpmp ) ) < 0 ) {
err = " pub addr req failed " ;
2009-05-30 00:14:57 +00:00
goto end ;
}
do {
2010-02-06 03:38:24 +00:00
struct timeval timeout = { 1 , 0 } ;
2009-06-02 22:46:38 +00:00
i + + ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Checking for PMP %d/%d \n " , i , max ) ;
2009-05-30 00:14:57 +00:00
FD_ZERO ( & fds ) ;
2009-06-05 15:01:54 +00:00
FD_SET ( natpmp . s , & fds ) ;
2009-07-30 16:20:47 +00:00
if ( ( r = getnatpmprequesttimeout ( & natpmp , & timeout ) ) < 0 ) {
err = " get timeout failed " ;
goto end ;
}
if ( ( r = select ( FD_SETSIZE , & fds , NULL , NULL , & timeout ) ) < 0 ) {
err = " select failed " ;
goto end ;
}
2009-06-05 15:01:54 +00:00
r = readnatpmpresponseorretry ( & natpmp , & response ) ;
2010-02-06 03:38:24 +00:00
} while ( r = = NATPMP_TRYAGAIN & & i < max ) ;
2009-05-30 00:14:57 +00:00
if ( r < 0 ) {
2009-07-30 16:20:47 +00:00
err = " general error " ;
2009-05-30 00:14:57 +00:00
goto end ;
}
pubaddr = inet_ntoa ( response . pnu . publicaddress . addr ) ;
2009-06-30 20:11:22 +00:00
switch_copy_string ( pub_addr , pubaddr , IP_LEN ) ;
2009-05-30 00:14:57 +00:00
nat_globals . nat_type = SWITCH_NAT_TYPE_PMP ;
2010-02-06 03:38:24 +00:00
2009-06-05 15:01:54 +00:00
closenatpmp ( & natpmp ) ;
2009-05-30 00:14:57 +00:00
2010-02-06 03:38:24 +00:00
end :
2009-05-30 00:14:57 +00:00
2009-07-30 16:20:47 +00:00
if ( err ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error checking for PMP [%s] \n " , err ) ;
}
2009-05-30 00:14:57 +00:00
return r ;
}
2009-06-30 18:59:05 +00:00
static int init_pmp ( void )
{
return get_pmp_pubaddr ( nat_globals . pub_addr ) ;
}
2011-04-01 17:30:24 +00:00
SWITCH_DECLARE ( void ) switch_nat_set_mapping ( switch_bool_t mapping )
{
nat_globals . mapping = mapping ;
}
2009-06-30 18:59:05 +00:00
SWITCH_DECLARE ( void ) switch_nat_reinit ( void )
{
2011-04-01 17:30:24 +00:00
switch_nat_init ( nat_globals_perm . pool , nat_globals . mapping ) ;
2009-06-30 18:59:05 +00:00
}
switch_status_t init_nat_monitor ( switch_memory_pool_t * pool )
{
char * addr = NULL ;
switch_port_t port = 0 ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( nat_globals . nat_type = = SWITCH_NAT_TYPE_UPNP ) {
addr = " 239.255.255.250 " ;
port = 1900 ;
} else if ( nat_globals . nat_type = = SWITCH_NAT_TYPE_PMP ) {
addr = " 224.0.0.1 " ;
port = 5350 ;
}
if ( switch_sockaddr_info_get ( & nat_globals_perm . maddress , addr , SWITCH_UNSPEC , port , 0 , pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Cannot find address \n " ) ;
return SWITCH_STATUS_TERM ;
}
if ( switch_socket_create ( & nat_globals_perm . msocket , AF_INET , SOCK_DGRAM , 0 , pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Socket Error \n " ) ;
return SWITCH_STATUS_TERM ;
}
if ( switch_socket_opt_set ( nat_globals_perm . msocket , SWITCH_SO_REUSEADDR , 1 ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Socket Option Error \n " ) ;
switch_socket_close ( nat_globals_perm . msocket ) ;
return SWITCH_STATUS_TERM ;
}
if ( switch_mcast_join ( nat_globals_perm . msocket , nat_globals_perm . maddress , NULL , NULL ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Multicast Error \n " ) ;
switch_socket_close ( nat_globals_perm . msocket ) ;
return SWITCH_STATUS_TERM ;
}
if ( switch_socket_bind ( nat_globals_perm . msocket , nat_globals_perm . maddress ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Bind Error \n " ) ;
switch_socket_close ( nat_globals_perm . msocket ) ;
return SWITCH_STATUS_TERM ;
}
switch_socket_opt_set ( nat_globals_perm . msocket , SWITCH_SO_NONBLOCK , TRUE ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " NAT thread configured \n " ) ;
return SWITCH_STATUS_SUCCESS ;
}
2010-02-06 03:38:24 +00:00
static void * SWITCH_THREAD_FUNC switch_nat_multicast_runtime ( switch_thread_t * thread , void * obj )
2009-06-30 18:59:05 +00:00
{
char * buf = NULL ;
2009-08-24 20:16:23 +00:00
char newip [ 16 ] = " " ;
2009-07-27 14:31:47 +00:00
char * pos ;
2009-06-30 18:59:05 +00:00
switch_event_t * event = NULL ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " NAT thread started \n " ) ;
buf = ( char * ) malloc ( MULTICAST_BUFFSIZE ) ;
switch_assert ( buf ) ;
nat_globals_perm . running = 1 ;
while ( nat_globals_perm . running = = 1 ) {
size_t len = MULTICAST_BUFFSIZE ;
switch_status_t status ;
switch_bool_t do_repub = SWITCH_FALSE ;
memset ( buf , 0 , len ) ;
status = switch_socket_recvfrom ( nat_globals_perm . maddress , nat_globals_perm . msocket , 0 , buf , & len ) ;
if ( ! len ) {
if ( SWITCH_STATUS_IS_BREAK ( status ) ) {
switch_yield ( 5000000 ) ;
continue ;
}
break ;
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( nat_globals . nat_type = = SWITCH_NAT_TYPE_UPNP ) {
/* look for our desc URL and servicetype in the packet */
2009-06-30 21:47:47 +00:00
if ( strstr ( buf , nat_globals . descURL ) & & ( buf = = NULL | | strstr ( buf , nat_globals . data . servicetype ) ) ) {
2009-07-27 14:31:47 +00:00
if ( ( pos = strstr ( buf , " NTS: " ) ) ) {
pos = pos + 4 ;
2010-02-06 03:38:24 +00:00
while ( * pos & & * pos = = ' ' ) {
2009-07-27 14:31:47 +00:00
pos + + ;
}
if ( ! strncmp ( pos , " ssdp:alive " , 10 ) ) {
2012-03-01 15:28:38 +00:00
/* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "got UPnP keep alive packet: \n%s\n", buf); */
2009-07-27 14:31:47 +00:00
/* did pub ip change */
2009-08-24 20:16:23 +00:00
newip [ 0 ] = ' \0 ' ;
2009-07-27 14:31:47 +00:00
if ( get_upnp_pubaddr ( newip ) ! = SWITCH_STATUS_SUCCESS ) {
2010-02-06 03:38:24 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING ,
" Unable to get current pubaddr after receiving UPnP keep alive packet. \n " ) ;
2009-07-27 14:31:47 +00:00
}
} else if ( ! strncmp ( pos , " ssdp:byebye " , 11 ) ) {
2010-02-06 03:38:24 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING ,
" got UPnP signoff packet. Your NAT gateway is probably going offline. \n " ) ;
2009-07-27 14:31:47 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " got UPnP signoff packet: \n %s \n " , buf ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " got UNKNOWN UPnP keep alive packet: \n %s \n " , buf ) ;
2009-06-30 18:59:05 +00:00
}
}
}
} else {
/* got some data in NAT-PMP mode, treat any data as a republish event */
if ( get_pmp_pubaddr ( newip ) < 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Unable to get current pubaddr after receiving UPnP keep alive packet. \n " ) ;
}
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( ( strlen ( newip ) > 0 ) & & strcmp ( newip , " 0.0.0.0 " ) & & strcmp ( newip , nat_globals . pub_addr ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Public IP changed from '%s' to '%s'. \n " , nat_globals . pub_addr , newip ) ;
do_repub = SWITCH_TRUE ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
switch_event_create ( & event , SWITCH_EVENT_TRAP ) ;
2011-02-22 02:17:58 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " condition " , " network-external-address-change " ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " network-external-address-previous-v4 " , nat_globals . pub_addr ) ;
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " network-external-address-change-v4 " , newip ) ;
2009-06-30 18:59:05 +00:00
switch_event_fire ( & event ) ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
switch_set_string ( nat_globals . pub_addr , newip ) ;
switch_nat_reinit ( ) ;
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( do_repub ) {
switch_nat_republish ( ) ;
2010-02-06 03:38:24 +00:00
}
2009-06-30 18:59:05 +00:00
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " NAT thread ending \n " ) ;
nat_globals_perm . running = 0 ;
switch_safe_free ( buf ) ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
return NULL ;
}
switch_thread_t * nat_thread_p = NULL ;
SWITCH_DECLARE ( void ) switch_nat_thread_start ( void )
{
switch_threadattr_t * thd_attr ;
if ( init_nat_monitor ( nat_globals_perm . pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to initialize NAT thread \n " ) ;
return ;
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
switch_threadattr_create ( & thd_attr , nat_globals_perm . pool ) ;
switch_threadattr_detach_set ( thd_attr , 1 ) ;
switch_thread_create ( & nat_thread_p , thd_attr , switch_nat_multicast_runtime , NULL , nat_globals_perm . pool ) ;
}
SWITCH_DECLARE ( void ) switch_nat_thread_stop ( void )
{
/* don't do anything if no thread ptr */
2010-02-06 03:38:24 +00:00
if ( ! nat_thread_p ) {
2009-06-30 18:59:05 +00:00
return ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Stopping NAT Task Thread \n " ) ;
if ( nat_globals_perm . running = = 1 ) {
int sanity = 0 ;
switch_status_t st ;
nat_globals_perm . running = - 1 ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
switch_thread_join ( & st , nat_thread_p ) ;
while ( nat_globals_perm . running ) {
2010-02-06 03:38:24 +00:00
switch_yield ( 1000000 ) ; /* can take up to 5s for the thread to terminate, so wait for 10 */
2009-06-30 18:59:05 +00:00
if ( + + sanity > 10 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Timed out waiting for NAT Task Thread to stop \n " ) ;
break ;
}
}
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
nat_thread_p = NULL ;
}
2011-04-01 17:30:24 +00:00
SWITCH_DECLARE ( void ) switch_nat_init ( switch_memory_pool_t * pool , switch_bool_t mapping )
2009-05-30 00:14:57 +00:00
{
2009-06-30 18:59:05 +00:00
/* try free dynamic data structures prior to resetting to 0 */
2010-02-06 03:38:24 +00:00
FreeUPNPUrls ( & nat_globals . urls ) ;
2009-06-30 18:59:05 +00:00
switch_safe_free ( nat_globals . descURL ) ;
2009-05-30 00:14:57 +00:00
2009-06-30 18:59:05 +00:00
memset ( & nat_globals , 0 , sizeof ( nat_globals ) ) ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( first_init ) {
memset ( & nat_globals_perm , 0 , sizeof ( nat_globals_perm ) ) ;
nat_globals_perm . pool = pool ;
}
2010-02-06 03:38:24 +00:00
2011-04-01 17:30:24 +00:00
nat_globals . mapping = mapping ;
2009-06-02 16:55:10 +00:00
switch_find_local_ip ( nat_globals . pvt_addr , sizeof ( nat_globals . pvt_addr ) , NULL , AF_INET ) ;
2009-05-30 00:14:57 +00:00
2009-06-02 22:03:33 +00:00
2009-06-02 22:46:38 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Scanning for NAT \n " ) ;
2009-06-02 22:03:33 +00:00
2009-05-30 00:14:57 +00:00
init_pmp ( ) ;
2009-06-02 22:46:38 +00:00
if ( ! nat_globals . nat_type ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Checking for UPnP \n " ) ;
2009-05-30 00:14:57 +00:00
init_upnp ( ) ;
}
2010-02-06 03:38:24 +00:00
2009-05-30 00:14:57 +00:00
if ( nat_globals . nat_type ) {
switch_core_set_variable ( " nat_public_addr " , nat_globals . pub_addr ) ;
switch_core_set_variable ( " nat_private_addr " , nat_globals . pvt_addr ) ;
switch_core_set_variable ( " nat_type " , nat_globals . nat_type = = SWITCH_NAT_TYPE_PMP ? " pmp " : " upnp " ) ;
2011-02-02 21:43:26 +00:00
strncpy ( nat_globals . nat_type_str , nat_globals . nat_type = = SWITCH_NAT_TYPE_PMP ? " pmp " : " upnp " , sizeof ( nat_globals . nat_type_str ) - 1 ) ;
2010-02-06 03:38:24 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " NAT detected type: %s, ExtIP: '%s' \n " ,
nat_globals . nat_type = = SWITCH_NAT_TYPE_PMP ? " pmp " : " upnp " , nat_globals . pub_addr ) ;
2009-06-30 18:59:05 +00:00
if ( ! nat_thread_p ) {
switch_nat_thread_start ( ) ;
}
2009-05-30 00:14:57 +00:00
} else {
2009-11-04 11:55:45 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " No PMP or UPnP NAT devices detected! \n " ) ;
2009-05-30 00:14:57 +00:00
}
2009-06-30 18:59:05 +00:00
first_init = SWITCH_FALSE ;
2010-01-12 00:40:43 +00:00
initialized = SWITCH_TRUE ;
2009-05-30 00:14:57 +00:00
}
2010-02-06 03:38:24 +00:00
static switch_status_t switch_nat_add_mapping_pmp ( switch_port_t port , switch_nat_ip_proto_t proto , switch_port_t * external_port )
2009-05-30 00:14:57 +00:00
{
switch_status_t status = SWITCH_STATUS_FALSE ;
natpmpresp_t response ;
int r ;
2009-06-05 15:01:54 +00:00
natpmp_t natpmp ;
2009-05-30 00:14:57 +00:00
2009-06-05 15:01:54 +00:00
initnatpmp ( & natpmp ) ;
2010-02-06 03:38:24 +00:00
2009-05-30 00:14:57 +00:00
if ( proto = = SWITCH_NAT_TCP ) {
2009-06-05 15:01:54 +00:00
sendnewportmappingrequest ( & natpmp , NATPMP_PROTOCOL_TCP , port , port , 31104000 ) ;
2009-12-11 01:20:26 +00:00
} else if ( proto = = SWITCH_NAT_UDP ) {
2009-06-05 15:01:54 +00:00
sendnewportmappingrequest ( & natpmp , NATPMP_PROTOCOL_UDP , port , port , 31104000 ) ;
2009-05-30 00:14:57 +00:00
}
do {
fd_set fds ;
2009-06-05 14:39:28 +00:00
struct timeval timeout = { 1 , 0 } ;
2009-05-30 00:14:57 +00:00
FD_ZERO ( & fds ) ;
2009-06-05 15:01:54 +00:00
FD_SET ( natpmp . s , & fds ) ;
getnatpmprequesttimeout ( & natpmp , & timeout ) ;
2009-05-30 00:14:57 +00:00
select ( FD_SETSIZE , & fds , NULL , NULL , & timeout ) ;
2009-06-05 15:01:54 +00:00
r = readnatpmpresponseorretry ( & natpmp , & response ) ;
2010-02-06 03:38:24 +00:00
} while ( r = = NATPMP_TRYAGAIN ) ;
2009-05-30 01:03:55 +00:00
if ( r = = 0 ) {
2010-06-30 16:11:21 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG1 , " mapped public port %hu protocol %s to localport %hu \n " ,
2009-05-30 01:03:55 +00:00
response . pnu . newportmapping . mappedpublicport ,
response . type = = NATPMP_RESPTYPE_UDPPORTMAPPING ? " UDP " :
2010-02-06 03:38:24 +00:00
( response . type = = NATPMP_RESPTYPE_TCPPORTMAPPING ? " TCP " : " UNKNOWN " ) , response . pnu . newportmapping . privateport ) ;
2009-06-05 02:30:44 +00:00
if ( external_port ) {
* external_port = response . pnu . newportmapping . mappedpublicport ;
} else if ( response . pnu . newportmapping . mappedpublicport ! = response . pnu . newportmapping . privateport ) {
2010-06-30 16:11:21 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG1 , " External port %hu protocol %s was not available, it was instead mapped to %hu \n " ,
2010-02-06 03:38:24 +00:00
response . pnu . newportmapping . privateport ,
response . type = = NATPMP_RESPTYPE_UDPPORTMAPPING ? " UDP " :
( response . type = = NATPMP_RESPTYPE_TCPPORTMAPPING ? " TCP " : " UNKNOWN " ) , response . pnu . newportmapping . mappedpublicport ) ;
2009-06-05 02:30:44 +00:00
}
2010-02-06 03:38:24 +00:00
2009-05-30 01:03:55 +00:00
status = SWITCH_STATUS_SUCCESS ;
}
2010-02-06 03:38:24 +00:00
2009-06-05 15:01:54 +00:00
closenatpmp ( & natpmp ) ;
2010-02-06 03:38:24 +00:00
2009-05-30 00:14:57 +00:00
return status ;
}
static switch_status_t switch_nat_add_mapping_upnp ( switch_port_t port , switch_nat_ip_proto_t proto )
{
switch_status_t status = SWITCH_STATUS_FALSE ;
2009-06-30 20:11:22 +00:00
char port_str [ IP_LEN ] ;
2009-05-30 00:56:32 +00:00
int r = UPNPCOMMAND_UNKNOWN_ERROR ;
2009-05-30 00:14:57 +00:00
sprintf ( port_str , " %d " , port ) ;
if ( proto = = SWITCH_NAT_TCP ) {
2009-05-30 03:24:10 +00:00
r = UPNP_AddPortMapping ( nat_globals . urls . controlURL , nat_globals . data . servicetype , port_str , port_str ,
nat_globals . pvt_addr , " FreeSWITCH " , " TCP " , 0 ) ;
2009-12-11 01:20:26 +00:00
} else if ( proto = = SWITCH_NAT_UDP ) {
2009-05-30 03:24:10 +00:00
r = UPNP_AddPortMapping ( nat_globals . urls . controlURL , nat_globals . data . servicetype , port_str , port_str ,
nat_globals . pvt_addr , " FreeSWITCH " , " UDP " , 0 ) ;
2009-05-30 00:14:57 +00:00
}
2009-05-30 00:56:32 +00:00
if ( r = = UPNPCOMMAND_SUCCESS ) {
2009-05-30 00:14:57 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " mapped public port %s protocol %s to localport %s \n " , port_str ,
( proto = = SWITCH_NAT_TCP ) ? " TCP " : ( proto = = SWITCH_NAT_UDP ? " UDP " : " UNKNOWN " ) , port_str ) ;
2010-02-06 03:38:24 +00:00
status = SWITCH_STATUS_SUCCESS ;
2009-05-30 00:14:57 +00:00
}
2009-05-30 01:03:55 +00:00
2009-05-30 00:14:57 +00:00
return status ;
}
static switch_status_t switch_nat_del_mapping_pmp ( switch_port_t port , switch_nat_ip_proto_t proto )
{
switch_status_t status = SWITCH_STATUS_FALSE ;
natpmpresp_t response ;
int r ;
2009-06-05 15:01:54 +00:00
natpmp_t natpmp ;
2010-02-06 03:38:24 +00:00
2009-06-05 15:01:54 +00:00
initnatpmp ( & natpmp ) ;
2009-05-30 00:14:57 +00:00
if ( proto = = SWITCH_NAT_TCP ) {
2009-06-05 15:01:54 +00:00
sendnewportmappingrequest ( & natpmp , NATPMP_PROTOCOL_TCP , port , port , 0 ) ;
2009-12-11 01:20:26 +00:00
} else if ( proto = = SWITCH_NAT_UDP ) {
2009-06-05 15:01:54 +00:00
sendnewportmappingrequest ( & natpmp , NATPMP_PROTOCOL_UDP , port , port , 0 ) ;
2009-05-30 00:14:57 +00:00
}
do {
fd_set fds ;
struct timeval timeout ;
FD_ZERO ( & fds ) ;
2009-06-05 15:01:54 +00:00
FD_SET ( natpmp . s , & fds ) ;
getnatpmprequesttimeout ( & natpmp , & timeout ) ;
2009-05-30 00:14:57 +00:00
select ( FD_SETSIZE , & fds , NULL , NULL , & timeout ) ;
2009-06-05 15:01:54 +00:00
r = readnatpmpresponseorretry ( & natpmp , & response ) ;
2010-02-06 03:38:24 +00:00
} while ( r = = NATPMP_TRYAGAIN ) ;
2009-05-30 01:03:55 +00:00
if ( r = = 0 ) {
2010-02-06 03:38:24 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " unmapped public port %hu protocol %s to localport %hu \n " , response . pnu . newportmapping . privateport , /* This might be wrong but its so 0 isn't displayed */
2009-05-30 01:03:55 +00:00
response . type = = NATPMP_RESPTYPE_UDPPORTMAPPING ? " UDP " :
2010-02-06 03:38:24 +00:00
( response . type = = NATPMP_RESPTYPE_TCPPORTMAPPING ? " TCP " : " UNKNOWN " ) , response . pnu . newportmapping . privateport ) ;
2009-05-30 01:03:55 +00:00
status = SWITCH_STATUS_SUCCESS ;
}
2010-02-06 03:38:24 +00:00
2009-06-05 15:01:54 +00:00
closenatpmp ( & natpmp ) ;
2009-05-30 00:14:57 +00:00
return status ;
}
static switch_status_t switch_nat_del_mapping_upnp ( switch_port_t port , switch_nat_ip_proto_t proto )
{
switch_status_t status = SWITCH_STATUS_FALSE ;
2009-06-30 20:11:22 +00:00
char port_str [ IP_LEN ] ;
2009-05-30 00:56:32 +00:00
int r = UPNPCOMMAND_UNKNOWN_ERROR ;
2009-05-30 00:14:57 +00:00
sprintf ( port_str , " %d " , port ) ;
if ( proto = = SWITCH_NAT_TCP ) {
2009-05-30 00:56:32 +00:00
r = UPNP_DeletePortMapping ( nat_globals . urls . controlURL , nat_globals . data . servicetype , port_str , " TCP " , 0 ) ;
2009-12-11 01:20:26 +00:00
} else if ( proto = = SWITCH_NAT_UDP ) {
2009-05-30 00:56:32 +00:00
r = UPNP_DeletePortMapping ( nat_globals . urls . controlURL , nat_globals . data . servicetype , port_str , " UDP " , 0 ) ;
2009-05-30 00:14:57 +00:00
}
2009-05-30 00:56:32 +00:00
if ( r = = UPNPCOMMAND_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " unmapped public port %s protocol %s to localport %s \n " , port_str ,
( proto = = SWITCH_NAT_TCP ) ? " TCP " : ( proto = = SWITCH_NAT_UDP ? " UDP " : " UNKNOWN " ) , port_str ) ;
status = SWITCH_STATUS_SUCCESS ;
}
2009-05-30 00:14:57 +00:00
return status ;
}
2011-02-02 21:43:26 +00:00
SWITCH_DECLARE ( const char * ) switch_nat_get_type ( void )
{
return nat_globals . nat_type_str ;
}
2010-02-06 03:38:24 +00:00
SWITCH_DECLARE ( switch_status_t ) switch_nat_add_mapping_internal ( switch_port_t port , switch_nat_ip_proto_t proto , switch_port_t * external_port ,
switch_bool_t sticky , switch_bool_t publish )
2009-05-30 00:14:57 +00:00
{
switch_status_t status = SWITCH_STATUS_FALSE ;
2009-06-30 18:59:05 +00:00
switch_event_t * event = NULL ;
2010-02-06 03:38:24 +00:00
2014-05-24 00:34:12 +00:00
if ( ! initialized | | ! nat_globals . nat_type ) return status ;
2011-04-01 17:30:24 +00:00
if ( ! nat_globals . mapping ) {
2011-04-19 19:20:28 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " NAT port mapping disabled \n " ) ;
2011-04-01 17:30:24 +00:00
return status ;
}
2009-05-30 00:14:57 +00:00
switch ( nat_globals . nat_type ) {
case SWITCH_NAT_TYPE_PMP :
2009-06-05 02:30:44 +00:00
status = switch_nat_add_mapping_pmp ( port , proto , external_port ) ;
2009-05-30 00:14:57 +00:00
break ;
case SWITCH_NAT_TYPE_UPNP :
2009-07-18 19:20:21 +00:00
if ( ( status = switch_nat_add_mapping_upnp ( port , proto ) ) = = SWITCH_STATUS_SUCCESS ) {
2009-06-05 02:30:44 +00:00
if ( external_port ) {
* external_port = port ;
2009-07-18 19:20:21 +00:00
}
2009-06-05 02:30:44 +00:00
}
2009-05-30 00:14:57 +00:00
break ;
default :
break ;
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( publish & & status = = SWITCH_STATUS_SUCCESS ) {
2010-02-06 03:38:24 +00:00
switch_event_create ( & event , SWITCH_EVENT_NAT ) ;
2009-06-30 18:59:05 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " op " , " add " ) ;
2009-07-18 19:20:21 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " port " , " %d " , port ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " proto " , " %d " , proto ) ;
2009-06-30 18:59:05 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " sticky " , ( sticky ? " true " : " false " ) ) ;
switch_event_fire ( & event ) ;
}
2009-05-30 00:14:57 +00:00
return status ;
}
2010-02-06 03:38:24 +00:00
SWITCH_DECLARE ( switch_status_t ) switch_nat_add_mapping ( switch_port_t port , switch_nat_ip_proto_t proto , switch_port_t * external_port ,
switch_bool_t sticky )
2009-06-30 18:59:05 +00:00
{
return switch_nat_add_mapping_internal ( port , proto , external_port , sticky , SWITCH_TRUE ) ;
}
2009-05-30 00:14:57 +00:00
SWITCH_DECLARE ( switch_status_t ) switch_nat_del_mapping ( switch_port_t port , switch_nat_ip_proto_t proto )
{
switch_status_t status = SWITCH_STATUS_FALSE ;
2009-06-30 18:59:05 +00:00
switch_event_t * event = NULL ;
2009-05-30 00:14:57 +00:00
switch ( nat_globals . nat_type ) {
case SWITCH_NAT_TYPE_PMP :
status = switch_nat_del_mapping_pmp ( port , proto ) ;
break ;
case SWITCH_NAT_TYPE_UPNP :
status = switch_nat_del_mapping_upnp ( port , proto ) ;
break ;
default :
break ;
}
2009-06-30 18:59:05 +00:00
if ( status = = SWITCH_STATUS_SUCCESS ) {
2010-02-06 03:38:24 +00:00
switch_event_create ( & event , SWITCH_EVENT_NAT ) ;
2009-06-30 18:59:05 +00:00
switch_event_add_header_string ( event , SWITCH_STACK_BOTTOM , " op " , " del " ) ;
2009-07-18 19:20:21 +00:00
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " port " , " %d " , port ) ;
switch_event_add_header ( event , SWITCH_STACK_BOTTOM , " proto " , " %d " , proto ) ;
2009-06-30 18:59:05 +00:00
switch_event_fire ( & event ) ;
}
2010-02-06 03:38:24 +00:00
2009-05-30 00:14:57 +00:00
return status ;
}
2009-06-30 18:59:05 +00:00
SWITCH_DECLARE ( void ) switch_nat_republish ( void )
2009-05-30 00:14:57 +00:00
{
2009-06-30 18:59:05 +00:00
switch_xml_t natxml = NULL ;
switch_xml_t row = NULL ;
switch_xml_t child = NULL ;
switch_stream_handle_t stream = { 0 } ;
SWITCH_STANDARD_STREAM ( stream ) ;
2009-06-05 15:01:54 +00:00
2010-06-30 16:11:21 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG1 , " Refreshing nat maps \n " ) ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
switch_api_execute ( " show " , " nat_map as xml " , NULL , & stream ) ;
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( ! ( natxml = switch_xml_parse_str_dup ( stream . data ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to parse XML: %s \n " , ( char * ) stream . data ) ;
switch_safe_free ( stream . data ) ;
2010-01-12 01:47:31 +00:00
return ;
2009-06-30 18:59:05 +00:00
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
/* iterate the xml and publish the mappings */
row = switch_xml_find_child ( natxml , " row " , " row_id " , " 1 " ) ;
while ( row ! = NULL ) {
char * sport = NULL ;
char * sproto = NULL ;
switch_port_t port ;
switch_nat_ip_proto_t proto ;
if ( ( child = switch_xml_child ( row , " port " ) ) ) {
sport = child - > txt ;
}
if ( ( child = switch_xml_child ( row , " proto_num " ) ) ) {
sproto = child - > txt ;
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
if ( sport & & sproto ) {
2010-02-06 03:38:24 +00:00
port = ( switch_port_t ) ( atoi ( sport ) ) ;
proto = ( switch_nat_ip_proto_t ) ( atoi ( sproto ) ) ;
switch_nat_add_mapping_internal ( port , proto , NULL , SWITCH_FALSE , SWITCH_FALSE ) ;
2009-06-30 18:59:05 +00:00
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to parse port/proto info: XML: %s \n " , ( char * ) stream . data ) ;
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
row = switch_xml_next ( row ) ;
}
2010-02-06 03:38:24 +00:00
2009-06-30 18:59:05 +00:00
switch_safe_free ( stream . data ) ;
switch_xml_free ( natxml ) ;
}
2010-06-21 00:21:42 +00:00
SWITCH_STANDARD_SCHED_FUNC ( switch_nat_republish_sched )
{
switch_nat_republish ( ) ;
if ( nat_globals_perm . running = = 1 ) {
task - > runtime = switch_epoch_time_now ( NULL ) + NAT_REFRESH_INTERVAL ;
}
}
SWITCH_DECLARE ( void ) switch_nat_late_init ( void )
{
if ( nat_globals_perm . running = = 1 ) {
switch_scheduler_add_task ( switch_epoch_time_now ( NULL ) + NAT_REFRESH_INTERVAL , switch_nat_republish_sched , " nat_republish " , " core " , 0 , NULL ,
2010-09-13 17:12:04 +00:00
SSHF_OWN_THREAD ) ;
2010-06-21 00:21:42 +00:00
}
}
2009-06-30 18:59:05 +00:00
SWITCH_DECLARE ( char * ) switch_nat_status ( void )
{
switch_stream_handle_t stream = { 0 } ;
SWITCH_STANDARD_STREAM ( stream ) ;
2010-02-06 03:38:24 +00:00
stream . write_function ( & stream , " Nat Type: %s, ExtIP: %s \n " ,
( nat_globals . nat_type = = SWITCH_NAT_TYPE_UPNP ) ? " UPNP " : ( nat_globals . nat_type = = SWITCH_NAT_TYPE_PMP ? " NAT-PMP " : " UNKNOWN " ) ,
nat_globals . pub_addr ) ;
2009-06-30 18:59:05 +00:00
2011-04-01 17:30:24 +00:00
if ( nat_globals . mapping ) {
stream . write_function ( & stream , " NAT port mapping enabled. \n " ) ;
} else {
stream . write_function ( & stream , " NAT port mapping disabled. \n " ) ;
}
2009-06-30 18:59:05 +00:00
switch_api_execute ( " show " , " nat_map " , NULL , & stream ) ;
2010-02-06 03:38:24 +00:00
return stream . data ; /* caller frees */
2009-06-30 18:59:05 +00:00
}
2010-01-12 00:40:43 +00:00
SWITCH_DECLARE ( switch_bool_t ) switch_nat_is_initialized ( void )
{
return initialized ;
}
2009-06-30 18:59:05 +00:00
SWITCH_DECLARE ( void ) switch_nat_shutdown ( void )
{
switch_nat_thread_stop ( ) ;
FreeUPNPUrls ( & nat_globals . urls ) ;
switch_safe_free ( nat_globals . descURL ) ;
2009-05-30 00:14:57 +00:00
}
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
2013-06-25 16:50:17 +00:00
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 noet :
2009-05-30 00:14:57 +00:00
*/