2012-04-22 23:47:37 +00:00
/* $Id: upnpredirect.c,v 1.72 2012/04/22 23:25:22 nanard Exp $ */
2011-09-28 19:13:20 +00:00
/* MiniUPnP project
* http : //miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
2012-03-05 04:44:02 +00:00
* ( c ) 2006 - 2012 Thomas Bernard
2011-09-28 19:13:20 +00:00
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
# include <stdlib.h>
# include <string.h>
# include <syslog.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <net/if.h>
# include <arpa/inet.h>
# include <stdio.h>
# include <ctype.h>
# include <unistd.h>
# include "config.h"
# include "upnpredirect.h"
# include "upnpglobalvars.h"
# include "upnpevents.h"
# if defined(USE_NETFILTER)
# include "netfilter/iptcrdr.h"
# endif
# if defined(USE_PF)
# include "pf/obsdrdr.h"
2012-04-20 14:53:34 +00:00
# include "pf/pfpinhole.h"
2011-09-28 19:13:20 +00:00
# endif
# if defined(USE_IPF)
# include "ipf/ipfrdr.h"
# endif
# if defined(USE_IPFW)
# include "ipfw/ipfwrdr.h"
# endif
# ifdef USE_MINIUPNPDCTL
# include <stdio.h>
# include <unistd.h>
# endif
# ifdef ENABLE_LEASEFILE
# include <sys/stat.h>
# endif
/* from <inttypes.h> */
# ifndef PRIu64
# define PRIu64 "llu"
# endif
2012-03-01 01:44:38 +00:00
/* proto_atoi()
2011-09-28 19:13:20 +00:00
* convert the string " UDP " or " TCP " to IPPROTO_UDP and IPPROTO_UDP */
static int
proto_atoi ( const char * protocol )
{
int proto = IPPROTO_TCP ;
if ( strcmp ( protocol , " UDP " ) = = 0 )
proto = IPPROTO_UDP ;
return proto ;
}
# ifdef ENABLE_LEASEFILE
static int
lease_file_add ( unsigned short eport ,
const char * iaddr ,
unsigned short iport ,
int proto ,
const char * desc ,
unsigned int timestamp )
{
FILE * fd ;
if ( lease_file = = NULL ) return 0 ;
fd = fopen ( lease_file , " a " ) ;
if ( fd = = NULL ) {
syslog ( LOG_ERR , " could not open lease file: %s " , lease_file ) ;
return - 1 ;
}
fprintf ( fd , " %s:%hu:%s:%hu:%u:%s \n " ,
( ( proto = = IPPROTO_TCP ) ? " TCP " : " UDP " ) , eport , iaddr , iport ,
timestamp , desc ) ;
fclose ( fd ) ;
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
return 0 ;
}
static int
lease_file_remove ( unsigned short eport , int proto )
{
FILE * fd , * fdt ;
int tmp ;
char buf [ 512 ] ;
char str [ 32 ] ;
char tmpfilename [ 128 ] ;
int str_size , buf_size ;
if ( lease_file = = NULL ) return 0 ;
if ( strlen ( lease_file ) + 7 > sizeof ( tmpfilename ) ) {
syslog ( LOG_ERR , " Lease filename is too long " ) ;
return - 1 ;
}
strncpy ( tmpfilename , lease_file , sizeof ( tmpfilename ) ) ;
strncat ( tmpfilename , " XXXXXX " , sizeof ( tmpfilename ) - strlen ( tmpfilename ) ) ;
fd = fopen ( lease_file , " r " ) ;
if ( fd = = NULL ) {
return 0 ;
}
snprintf ( str , sizeof ( str ) , " %s:%u " , ( ( proto = = IPPROTO_TCP ) ? " TCP " : " UDP " ) , eport ) ;
str_size = strlen ( str ) ;
tmp = mkstemp ( tmpfilename ) ;
if ( tmp = = - 1 ) {
fclose ( fd ) ;
syslog ( LOG_ERR , " could not open temporary lease file " ) ;
return - 1 ;
}
fchmod ( tmp , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ;
fdt = fdopen ( tmp , " a " ) ;
buf [ sizeof ( buf ) - 1 ] = 0 ;
while ( fgets ( buf , sizeof ( buf ) - 1 , fd ) ! = NULL ) {
buf_size = strlen ( buf ) ;
if ( buf_size < str_size | | strncmp ( str , buf , str_size ) ! = 0 ) {
fwrite ( buf , buf_size , 1 , fdt ) ;
}
}
fclose ( fdt ) ;
fclose ( fd ) ;
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
if ( rename ( tmpfilename , lease_file ) < 0 ) {
syslog ( LOG_ERR , " could not rename temporary lease file to %s " , lease_file ) ;
remove ( tmpfilename ) ;
}
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
return 0 ;
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
}
/* reload_from_lease_file()
* read lease_file and add the rules contained
*/
int reload_from_lease_file ( )
{
FILE * fd ;
char * p ;
unsigned short eport , iport ;
char * proto ;
char * iaddr ;
char * desc ;
char * rhost ;
unsigned int leaseduration ;
unsigned int timestamp ;
time_t current_time ;
char line [ 128 ] ;
int r ;
if ( ! lease_file ) return - 1 ;
fd = fopen ( lease_file , " r " ) ;
if ( fd = = NULL ) {
syslog ( LOG_ERR , " could not open lease file: %s " , lease_file ) ;
return - 1 ;
}
if ( unlink ( lease_file ) < 0 ) {
syslog ( LOG_WARNING , " could not unlink file %s : %m " , lease_file ) ;
}
current_time = time ( NULL ) ;
while ( fgets ( line , sizeof ( line ) , fd ) ) {
syslog ( LOG_DEBUG , " parsing lease file line '%s' " , line ) ;
proto = line ;
p = strchr ( line , ' : ' ) ;
if ( ! p ) {
syslog ( LOG_ERR , " unrecognized data in lease file " ) ;
continue ;
}
* ( p + + ) = ' \0 ' ;
iaddr = strchr ( p , ' : ' ) ;
if ( ! iaddr ) {
syslog ( LOG_ERR , " unrecognized data in lease file " ) ;
continue ;
}
* ( iaddr + + ) = ' \0 ' ;
eport = ( unsigned short ) atoi ( p ) ;
p = strchr ( iaddr , ' : ' ) ;
if ( ! p ) {
syslog ( LOG_ERR , " unrecognized data in lease file " ) ;
continue ;
}
* ( p + + ) = ' \0 ' ;
2012-03-05 04:44:02 +00:00
iport = ( unsigned short ) atoi ( p ) ;
2011-09-28 19:13:20 +00:00
p = strchr ( p , ' : ' ) ;
if ( ! p ) {
syslog ( LOG_ERR , " unrecognized data in lease file " ) ;
continue ;
}
* ( p + + ) = ' \0 ' ;
desc = strchr ( p , ' : ' ) ;
if ( ! desc ) {
syslog ( LOG_ERR , " unrecognized data in lease file " ) ;
continue ;
}
* ( desc + + ) = ' \0 ' ;
2012-03-05 04:44:02 +00:00
timestamp = ( unsigned int ) atoi ( p ) ;
2011-09-28 19:13:20 +00:00
/* trim description */
while ( isspace ( * desc ) )
desc + + ;
p = desc ;
while ( * ( p + 1 ) )
p + + ;
while ( isspace ( * p ) & & ( p > desc ) )
* ( p - - ) = ' \0 ' ;
if ( timestamp > 0 ) {
if ( timestamp < = current_time ) {
syslog ( LOG_NOTICE , " already expired lease in lease file " ) ;
continue ;
} else {
2012-03-05 04:44:02 +00:00
leaseduration = timestamp - current_time ;
2011-09-28 19:13:20 +00:00
}
} else {
leaseduration = 0 ; /* default value */
}
rhost = NULL ;
r = upnp_redirect ( rhost , eport , iaddr , iport , proto , desc , leaseduration ) ;
if ( r = = - 1 ) {
syslog ( LOG_ERR , " Failed to redirect %hu -> %s:%hu protocol %s " ,
eport , iaddr , iport , proto ) ;
} else if ( r = = - 2 ) {
/* Add the redirection again to the lease file */
lease_file_add ( eport , iaddr , iport , proto_atoi ( proto ) ,
desc , timestamp ) ;
}
}
fclose ( fd ) ;
2012-03-01 01:44:38 +00:00
2011-09-28 19:13:20 +00:00
return 0 ;
}
# endif
2012-03-01 01:44:38 +00:00
/* upnp_redirect()
2011-09-28 19:13:20 +00:00
* calls OS / fw dependant implementation of the redirection .
* protocol should be the string " TCP " or " UDP "
* returns : 0 on success
* - 1 failed to redirect
* - 2 already redirected
* - 3 permission check failed
*/
int
2012-03-01 01:44:38 +00:00
upnp_redirect ( const char * rhost , unsigned short eport ,
2011-09-28 19:13:20 +00:00
const char * iaddr , unsigned short iport ,
const char * protocol , const char * desc ,
unsigned int leaseduration )
{
int proto , r ;
char iaddr_old [ 32 ] ;
unsigned short iport_old ;
struct in_addr address ;
unsigned int timestamp ;
proto = proto_atoi ( protocol ) ;
if ( inet_aton ( iaddr , & address ) < 0 ) {
syslog ( LOG_ERR , " inet_aton(%s) : %m " , iaddr ) ;
return - 1 ;
}
if ( ! check_upnp_rule_against_permissions ( upnppermlist , num_upnpperm ,
eport , address , iport ) ) {
syslog ( LOG_INFO , " redirection permission check failed for "
" %hu->%s:%hu %s " , eport , iaddr , iport , protocol ) ;
return - 3 ;
}
r = get_redirect_rule ( ext_if_name , eport , proto ,
iaddr_old , sizeof ( iaddr_old ) , & iport_old , 0 , 0 ,
0 , 0 ,
& timestamp , 0 , 0 ) ;
if ( r = = 0 ) {
/* if existing redirect rule matches redirect request return success
* xbox 360 does not keep track of the port it redirects and will
* redirect another port when receiving ConflictInMappingEntry */
if ( strcmp ( iaddr , iaddr_old ) = = 0 & & iport = = iport_old ) {
syslog ( LOG_INFO , " ignoring redirect request as it matches existing redirect " ) ;
} else {
syslog ( LOG_INFO , " port %hu protocol %s already redirected to %s:%hu " ,
eport , protocol , iaddr_old , iport_old ) ;
return - 2 ;
}
} else {
timestamp = ( leaseduration > 0 ) ? time ( NULL ) + leaseduration : 0 ;
syslog ( LOG_INFO , " redirecting port %hu to %s:%hu protocol %s for: %s " ,
2012-03-01 01:44:38 +00:00
eport , iaddr , iport , protocol , desc ) ;
2011-09-28 19:13:20 +00:00
return upnp_redirect_internal ( rhost , eport , iaddr , iport , proto ,
desc , timestamp ) ;
}
return 0 ;
}
int
upnp_redirect_internal ( const char * rhost , unsigned short eport ,
const char * iaddr , unsigned short iport ,
int proto , const char * desc ,
unsigned int timestamp )
{
/*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
eport , iaddr , iport , protocol , desc ) ; */
if ( add_redirect_rule2 ( ext_if_name , rhost , eport , iaddr , iport , proto ,
desc , timestamp ) < 0 ) {
return - 1 ;
}
# ifdef ENABLE_LEASEFILE
lease_file_add ( eport , iaddr , iport , proto , desc , timestamp ) ;
# endif
/* syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
iaddr , iport , protocol , desc ) ; */
if ( add_filter_rule2 ( ext_if_name , rhost , iaddr , eport , iport , proto , desc ) < 0 ) {
/* clean up the redirect rule */
# if !defined(__linux__)
delete_redirect_rule ( ext_if_name , eport , proto ) ;
# endif
return - 1 ;
}
if ( timestamp > 0 ) {
if ( ! nextruletoclean_timestamp | | ( timestamp < nextruletoclean_timestamp ) )
nextruletoclean_timestamp = timestamp ;
}
# ifdef ENABLE_EVENTS
/* the number of port mappings changed, we must
* inform the subscribers */
upnp_event_var_change_notify ( EWanIPC ) ;
# endif
return 0 ;
}
/* Firewall independant code which call the FW dependant code. */
int
upnp_get_redirection_infos ( unsigned short eport , const char * protocol ,
unsigned short * iport ,
char * iaddr , int iaddrlen ,
char * desc , int desclen ,
char * rhost , int rhostlen ,
unsigned int * leaseduration )
{
int r ;
unsigned int timestamp ;
time_t current_time ;
if ( desc & & ( desclen > 0 ) )
desc [ 0 ] = ' \0 ' ;
if ( rhost & & ( rhostlen > 0 ) )
rhost [ 0 ] = ' \0 ' ;
r = get_redirect_rule ( ext_if_name , eport , proto_atoi ( protocol ) ,
iaddr , iaddrlen , iport , desc , desclen ,
rhost , rhostlen , & timestamp ,
0 , 0 ) ;
if ( r = = 0 & & timestamp > 0 & & timestamp > ( current_time = time ( NULL ) ) ) {
* leaseduration = timestamp - current_time ;
} else {
* leaseduration = 0 ;
}
return r ;
}
int
upnp_get_redirection_infos_by_index ( int index ,
unsigned short * eport , char * protocol ,
2012-03-01 01:44:38 +00:00
unsigned short * iport ,
2011-09-28 19:13:20 +00:00
char * iaddr , int iaddrlen ,
char * desc , int desclen ,
char * rhost , int rhostlen ,
unsigned int * leaseduration )
{
/*char ifname[IFNAMSIZ];*/
int proto = 0 ;
unsigned int timestamp ;
time_t current_time ;
if ( desc & & ( desclen > 0 ) )
desc [ 0 ] = ' \0 ' ;
if ( rhost & & ( rhost > 0 ) )
rhost [ 0 ] = ' \0 ' ;
if ( get_redirect_rule_by_index ( index , 0 /*ifname*/ , eport , iaddr , iaddrlen ,
iport , & proto , desc , desclen ,
rhost , rhostlen , & timestamp ,
0 , 0 ) < 0 )
return - 1 ;
else
{
current_time = time ( NULL ) ;
* leaseduration = ( timestamp > current_time )
? ( timestamp - current_time )
: 0 ;
if ( proto = = IPPROTO_TCP )
memcpy ( protocol , " TCP " , 4 ) ;
else
memcpy ( protocol , " UDP " , 4 ) ;
return 0 ;
}
}
/* called from natpmp.c too */
int
_upnp_delete_redir ( unsigned short eport , int proto )
{
int r ;
# if defined(__linux__)
r = delete_redirect_and_filter_rules ( eport , proto ) ;
# else
r = delete_redirect_rule ( ext_if_name , eport , proto ) ;
delete_filter_rule ( ext_if_name , eport , proto ) ;
# endif
# ifdef ENABLE_LEASEFILE
lease_file_remove ( eport , proto ) ;
# endif
# ifdef ENABLE_EVENTS
upnp_event_var_change_notify ( EWanIPC ) ;
# endif
return r ;
}
int
upnp_delete_redirection ( unsigned short eport , const char * protocol )
{
syslog ( LOG_INFO , " removing redirect rule port %hu %s " , eport , protocol ) ;
return _upnp_delete_redir ( eport , proto_atoi ( protocol ) ) ;
}
/* upnp_get_portmapping_number_of_entries()
* TODO : improve this code . */
int
upnp_get_portmapping_number_of_entries ( )
{
int n = 0 , r = 0 ;
unsigned short eport , iport ;
char protocol [ 4 ] , iaddr [ 32 ] , desc [ 64 ] , rhost [ 32 ] ;
unsigned int leaseduration ;
do {
protocol [ 0 ] = ' \0 ' ; iaddr [ 0 ] = ' \0 ' ; desc [ 0 ] = ' \0 ' ;
r = upnp_get_redirection_infos_by_index ( n , & eport , protocol , & iport ,
iaddr , sizeof ( iaddr ) ,
desc , sizeof ( desc ) ,
rhost , sizeof ( rhost ) ,
& leaseduration ) ;
n + + ;
} while ( r = = 0 ) ;
return ( n - 1 ) ;
}
/* functions used to remove unused rules
* As a side effect , delete expired rules ( based on LeaseDuration ) */
struct rule_state *
get_upnp_rules_state_list ( int max_rules_number_target )
{
/*char ifname[IFNAMSIZ];*/
int proto ;
unsigned short iport ;
unsigned int timestamp ;
struct rule_state * tmp ;
struct rule_state * list = 0 ;
struct rule_state * * p ;
int i = 0 ;
time_t current_time ;
/*ifname[0] = '\0';*/
tmp = malloc ( sizeof ( struct rule_state ) ) ;
if ( ! tmp )
return 0 ;
current_time = time ( NULL ) ;
nextruletoclean_timestamp = 0 ;
while ( get_redirect_rule_by_index ( i , /*ifname*/ 0 , & tmp - > eport , 0 , 0 ,
& iport , & proto , 0 , 0 , 0 , 0 , & timestamp ,
& tmp - > packets , & tmp - > bytes ) > = 0 )
{
tmp - > to_remove = 0 ;
if ( timestamp > 0 ) {
/* need to remove this port mapping ? */
if ( timestamp < = current_time )
tmp - > to_remove = 1 ;
else if ( ( nextruletoclean_timestamp < = current_time )
| | ( timestamp < nextruletoclean_timestamp ) )
nextruletoclean_timestamp = timestamp ;
}
tmp - > proto = ( short ) proto ;
/* add tmp to list */
tmp - > next = list ;
list = tmp ;
/* prepare next iteration */
i + + ;
tmp = malloc ( sizeof ( struct rule_state ) ) ;
if ( ! tmp )
break ;
}
free ( tmp ) ;
/* remove the redirections that need to be removed */
for ( p = & list , tmp = list ; tmp ; tmp = * p )
{
if ( tmp - > to_remove )
{
syslog ( LOG_NOTICE , " remove port mapping %hu %s because it has expired " ,
tmp - > eport , ( tmp - > proto = = IPPROTO_TCP ) ? " TCP " : " UDP " ) ;
_upnp_delete_redir ( tmp - > eport , tmp - > proto ) ;
* p = tmp - > next ;
free ( tmp ) ;
i - - ;
} else {
p = & ( tmp - > next ) ;
}
}
/* return empty list if not enough redirections */
if ( i < = max_rules_number_target )
while ( list )
{
tmp = list ;
list = tmp - > next ;
free ( tmp ) ;
}
/* return list */
return list ;
}
void
remove_unused_rules ( struct rule_state * list )
{
char ifname [ IFNAMSIZ ] ;
unsigned short iport ;
struct rule_state * tmp ;
u_int64_t packets ;
u_int64_t bytes ;
unsigned int timestamp ;
int n = 0 ;
while ( list )
{
/* remove the rule if no traffic has used it */
if ( get_redirect_rule ( ifname , list - > eport , list - > proto ,
0 , 0 , & iport , 0 , 0 , 0 , 0 , & timestamp ,
& packets , & bytes ) > = 0 )
{
if ( packets = = list - > packets & & bytes = = list - > bytes )
{
_upnp_delete_redir ( list - > eport , list - > proto ) ;
n + + ;
}
}
tmp = list ;
list = tmp - > next ;
free ( tmp ) ;
}
if ( n > 0 )
syslog ( LOG_NOTICE , " removed %d unused rules " , n ) ;
}
/* upnp_get_portmappings_in_range()
* return a list of all " external " ports for which a port
* mapping exists */
unsigned short *
upnp_get_portmappings_in_range ( unsigned short startport ,
unsigned short endport ,
const char * protocol ,
unsigned int * number )
{
int proto ;
proto = proto_atoi ( protocol ) ;
if ( ! number )
return NULL ;
return get_portmappings_in_range ( startport , endport , proto , number ) ;
}
# ifdef ENABLE_6FC_SERVICE
int
upnp_check_outbound_pinhole ( int proto , int * timeout )
{
#if 0
int s , tmptimeout , tmptime_out ;
switch ( proto )
{
case IPPROTO_UDP :
s = retrieve_timeout ( " udp_timeout " , timeout ) ;
return s ;
break ;
case IPPROTO_UDPLITE :
s = retrieve_timeout ( " udp_timeout_stream " , timeout ) ;
return s ;
break ;
case IPPROTO_TCP :
s = retrieve_timeout ( " tcp_timeout_established " , timeout ) ;
return s ;
break ;
case 65535 :
s = retrieve_timeout ( " udp_timeout " , timeout ) ;
s = retrieve_timeout ( " udp_timeout_stream " , & tmptimeout ) ;
s = retrieve_timeout ( " tcp_timeout_established " , & tmptime_out ) ;
if ( tmptimeout < tmptime_out )
{
if ( tmptimeout < * timeout )
* timeout = tmptimeout ;
}
else
{
if ( tmptime_out < * timeout )
* timeout = tmptimeout ;
}
return s ;
break ;
default :
return - 5 ;
break ;
}
# endif
return 0 ;
}
/* upnp_add_inboundpinhole()
* returns : 0 on success
* - 1 failed to add pinhole
* - 2 already created
* - 3 inbound pinhole disabled
2012-04-20 14:53:34 +00:00
* TODO : return uid on success ( positive ) or error value ( negative )
2011-09-28 19:13:20 +00:00
*/
int
upnp_add_inboundpinhole ( const char * raddr ,
unsigned short rport ,
const char * iaddr ,
unsigned short iport ,
2012-04-20 22:09:52 +00:00
int proto ,
2012-04-20 14:53:34 +00:00
unsigned int leasetime ,
2011-09-28 19:13:20 +00:00
int * uid )
{
2012-04-20 14:53:34 +00:00
int r ;
2012-04-14 22:34:57 +00:00
#if 0
2012-04-20 14:53:34 +00:00
char iaddr_old [ 40 ] = " " , idfound [ 5 ] = " " ; /* IPv6 Modification*/
unsigned short iport_old = 0 ;
# endif
time_t current ;
unsigned int timestamp ;
2012-04-14 22:34:57 +00:00
struct in6_addr address ; /* IPv6 Modification*/
2012-04-20 14:53:34 +00:00
2012-04-14 22:34:57 +00:00
if ( inet_pton ( AF_INET6 , iaddr , & address ) < 0 ) /* IPv6 Modification */
2011-09-28 19:13:20 +00:00
{
syslog ( LOG_ERR , " inet_pton(%s) : %m " , iaddr ) ;
return 0 ;
2012-04-14 22:34:57 +00:00
}
2012-04-20 14:53:34 +00:00
current = time ( NULL ) ;
timestamp = current + leasetime ;
2011-09-28 19:13:20 +00:00
#if 0
r = get_rule_from_file ( raddr , rport , iaddr_old , & iport_old , proto , 0 , 0 , idfound ) ;
# endif
r = 0 ;
2012-04-20 14:53:34 +00:00
#if 0
2011-09-28 19:13:20 +00:00
if ( r = = 1 & & strcmp ( iaddr , iaddr_old ) = = 0 & & iport = = iport_old )
{
syslog ( LOG_INFO , " Pinhole for inbound traffic from [%s]:%hu to [%s]:%hu with protocol %s already done. Updating it. " , raddr , rport , iaddr_old , iport_old , protocol ) ;
t = upnp_update_inboundpinhole ( idfound , leaseTime ) ;
* uid = atoi ( idfound ) ;
return t ;
}
else
2012-04-20 14:53:34 +00:00
# endif
2011-09-28 19:13:20 +00:00
{
2012-04-20 22:09:52 +00:00
syslog ( LOG_INFO , " Adding pinhole for inbound traffic from [%s]:%hu to [%s]:%hu with proto %d and %u lease time. " , raddr , rport , iaddr , iport , proto , leasetime ) ;
2012-04-20 14:53:34 +00:00
# ifdef USE_PF
* uid = add_pinhole ( 0 /*ext_if_name*/ , raddr , rport , iaddr , iport , proto , timestamp ) ;
return 1 ;
# else
return - 42 ; /* not implemented */
# endif
2011-09-28 19:13:20 +00:00
#if 0
2012-04-20 14:53:34 +00:00
s = upnp_add_inboundpinhole_internal ( raddr , rport , iaddr , iport , protocol , uid ) ;
2011-09-28 19:13:20 +00:00
if ( rule_file_add ( raddr , rport , iaddr , iport , protocol , leaseTmp , uid ) < 0 )
return - 8 ;
else
{
if ( nextpinholetoclean_timestamp = = 0 | | ( atoi ( leaseTmp ) < = nextpinholetoclean_timestamp ) )
{
printf ( " Initializing the nextpinholetoclean variables. uid = %d \n " , * uid ) ;
snprintf ( nextpinholetoclean_uid , 5 , " %.4d " , * uid ) ;
nextpinholetoclean_timestamp = atoi ( leaseTmp ) ;
}
return s ;
}
# endif
}
return 0 ;
}
int
upnp_add_inboundpinhole_internal ( const char * raddr , unsigned short rport ,
const char * iaddr , unsigned short iport ,
const char * proto , int * uid )
{
int c = 9999 ;
char cmd [ 256 ] , cmd_raw [ 256 ] , cuid [ 42 ] ;
#if 0
static const char cmdval_full_udptcp [ ] = " ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT " ;
static const char cmdval_udptcp [ ] = " ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT " ;
static const char cmdval_full_udplite [ ] = " ip6tables -I %s %d -p %s -i %s -s %s -d %s -j ACCEPT " ;
static const char cmdval_udplite [ ] = " ip6tables -I %s %d -p %s -i %s -d %s -j ACCEPT " ;
// raw table command
static const char cmdval_full_udptcp_raw [ ] = " ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE " ;
static const char cmdval_udptcp_raw [ ] = " ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE " ;
static const char cmdval_full_udplite_raw [ ] = " ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s -d %s -j TRACE " ;
static const char cmdval_udplite_raw [ ] = " ip6tables -t raw -I PREROUTING %d -p %s -i %s -d %s -j TRACE " ;
# endif
2012-04-14 22:34:57 +00:00
/*printf("%s\n", raddr);*/
2011-09-28 19:13:20 +00:00
if ( raddr ! = NULL )
{
# ifdef IPPROTO_UDPLITE
if ( atoi ( proto ) = = IPPROTO_UDPLITE )
{
/* snprintf(cmd, sizeof(cmd), cmdval_full_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, iaddr);
snprintf ( cmd_raw , sizeof ( cmd_raw ) , cmdval_full_udplite_raw , line_number , proto , ext_if_name , raddr , iaddr ) ; */
}
else
# endif
{
/* snprintf(cmd, sizeof(cmd), cmdval_full_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport);
snprintf ( cmd_raw , sizeof ( cmd_raw ) , cmdval_full_udptcp_raw , line_number , proto , ext_if_name , raddr , rport , iaddr , iport ) ; */
}
}
else
{
# ifdef IPPROTO_UDPLITE
if ( atoi ( proto ) = = IPPROTO_UDPLITE )
{
/*snprintf(cmd, sizeof(cmd), cmdval_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, iaddr);
snprintf ( cmd_raw , sizeof ( cmd_raw ) , cmdval_udplite_raw , line_number , proto , ext_if_name , iaddr ) ; */
}
else
# endif
{
/*snprintf(cmd, sizeof(cmd), cmdval_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, rport, iaddr, iport);
snprintf ( cmd_raw , sizeof ( cmd_raw ) , cmdval_udptcp_raw , line_number , proto , ext_if_name , rport , iaddr , iport ) ;
*/
}
}
# ifdef DEBUG
syslog ( LOG_INFO , " Adding following ip6tables rule: " ) ;
syslog ( LOG_INFO , " -> %s " , cmd ) ;
syslog ( LOG_INFO , " -> %s " , cmd_raw ) ;
# endif
2012-04-14 22:34:57 +00:00
/* TODO Add a better checking error.*/
2011-09-28 19:13:20 +00:00
if ( system ( cmd ) < 0 | | system ( cmd_raw ) < 0 )
{
return 0 ;
}
srand ( time ( NULL ) ) ;
snprintf ( cuid , sizeof ( cuid ) , " %.4d " , rand ( ) % c ) ;
* uid = atoi ( cuid ) ;
printf ( " \t _add_ uid: %s \n " , cuid ) ;
return 1 ;
}
2012-04-22 23:47:37 +00:00
/* upnp_get_pinhole_info()
* return values :
* 0 OK
* - 1 Internal error
* - 2 NOT FOUND ( no such entry )
* . .
* - 42 Not implemented
*/
2011-09-28 19:13:20 +00:00
int
2012-04-22 00:58:27 +00:00
upnp_get_pinhole_info ( unsigned short uid ,
char * raddr , int raddrlen ,
unsigned short * rport ,
char * iaddr , int iaddrlen ,
2011-09-28 19:13:20 +00:00
unsigned short * iport ,
2012-04-22 00:58:27 +00:00
int * proto ,
2012-04-22 23:47:37 +00:00
unsigned int * leasetime ,
unsigned int * packets )
2011-09-28 19:13:20 +00:00
{
2012-04-22 00:58:27 +00:00
/* Call Firewall specific code to get IPv6 pinhole infos */
# ifdef USE_PF
int r ;
unsigned int timestamp ;
2012-04-22 23:47:37 +00:00
u_int64_t packets_tmp , bytes_tmp ;
2012-04-22 00:58:27 +00:00
r = get_pinhole ( uid , raddr , raddrlen , rport ,
2012-04-22 23:47:37 +00:00
iaddr , iaddrlen , iport , proto , & timestamp ,
& packets_tmp , & bytes_tmp ) ;
2012-04-22 00:58:27 +00:00
if ( r > = 0 ) {
time_t current_time ;
current_time = time ( NULL ) ;
if ( timestamp > current_time )
* leasetime = timestamp - current_time ;
else
* leasetime = 0 ;
2012-04-22 23:47:37 +00:00
if ( packets )
* packets = ( unsigned int ) packets_tmp ;
2012-04-22 00:58:27 +00:00
}
return r ;
# else
return - 42 ; /* not implemented */
# endif
2011-09-28 19:13:20 +00:00
}
int
upnp_update_inboundpinhole ( const char * uid , const char * leasetime )
{
/* TODO : to be implemented */
#if 0
int r , n ;
syslog ( LOG_INFO , " Updating pinhole for inbound traffic with ID: %s " , uid ) ;
r = check_rule_from_file ( uid , 0 ) ;
if ( r < 0 )
return r ;
else
{
n = rule_file_update ( uid , leasetime ) ;
upnp_update_expiredpinhole ( ) ;
return n ;
}
# else
return - 1 ;
# endif
}
int
2012-04-22 00:58:27 +00:00
upnp_delete_inboundpinhole ( unsigned short uid )
2011-09-28 19:13:20 +00:00
{
2012-04-20 14:53:34 +00:00
# ifdef USE_PF
2012-04-22 00:58:27 +00:00
return delete_pinhole ( uid ) ;
2011-09-28 19:13:20 +00:00
# else
2012-04-20 14:53:34 +00:00
return - 1 ;
2011-09-28 19:13:20 +00:00
# endif
}
/*
* Result :
* 1 : Found Result
* - 4 : No result
* - 5 : Result in another table
* - 6 : Result in another chain
* - 7 : Result in a chain not a rule
*/
int
upnp_check_pinhole_working ( const char * uid ,
char * eaddr ,
char * iaddr ,
unsigned short * eport ,
unsigned short * iport ,
char * protocol ,
int * rulenum_used )
{
/* TODO : to be implemented */
#if 0
FILE * fd ;
time_t expire = time ( NULL ) ;
char buf [ 1024 ] , filename [ ] = " /var/log/kern.log " , expire_time [ 32 ] = " " ;
int res = - 4 , str_len ;
str_len = strftime ( expire_time , sizeof ( expire_time ) , " %b %d %H:%M:%S " , localtime ( & expire ) ) ;
fd = fopen ( filename , " r " ) ;
if ( fd = = NULL )
{
syslog ( LOG_ERR , " Get_rule: could not open file: %s " , filename ) ;
return - 1 ;
}
syslog ( LOG_INFO , " Get_rule: Starting getting info in file %s for %s \n " , filename , uid ) ;
buf [ sizeof ( buf ) - 1 ] = 0 ;
while ( fgets ( buf , sizeof ( buf ) - 1 , fd ) ! = NULL & & res ! = 1 )
{
//printf("line: %s\n", buf);
char * r , * t , * c , * p ;
// looking for something like filter:FORWARD:rule: or filter:MINIUPNPD:rule:
r = strstr ( buf , " :rule: " ) ;
p = strstr ( buf , " :policy: " ) ;
t = strstr ( buf , " TRACE: " ) ; // table pointeur
t + = 7 ;
c = t + 7 ; // chain pointeur
if ( r )
{
printf ( " \t ** Found %.*s \n " , 24 , t ) ;
char * src , * dst , * sport , * dport , * proto , * line ;
char time [ 15 ] = " " , src_addr [ 40 ] , dst_addr [ 40 ] , proto_tmp [ 8 ] ;
int proto_int ;
strncpy ( time , buf , sizeof ( time ) ) ;
/*if(compare_time(time, expire_time)<0)
{
printf ( " \t \t Not corresponding time \n " ) ;
continue ;
} */
line = r + 6 ;
printf ( " \t rule line = %d \n " , atoi ( line ) ) ;
src = strstr ( buf , " SRC= " ) ;
src + = 4 ;
snprintf ( src_addr , sizeof ( src_addr ) , " %.*s " , 39 , src ) ;
#if 0
del_char ( src_addr ) ;
add_char ( src_addr ) ;
# endif
dst = strstr ( buf , " DST= " ) ;
dst + = 4 ;
snprintf ( dst_addr , sizeof ( dst_addr ) , " %.*s " , 39 , dst ) ;
#if 0
del_char ( dst_addr ) ;
add_char ( dst_addr ) ;
# endif
proto = strstr ( buf , " PROTO= " ) ;
proto + = 6 ;
proto_int = atoi ( protocol ) ;
if ( proto_int = = IPPROTO_UDP )
strcpy ( proto_tmp , " UDP " ) ;
else if ( proto_int = = IPPROTO_TCP )
strcpy ( proto_tmp , " TCP " ) ;
# ifdef IPPROTO_UDPLITE
else if ( proto_int = = IPPROTO_UDPLITE )
strcpy ( proto_tmp , " UDPLITE " ) ;
# endif
else
strcpy ( proto_tmp , " UnsupportedProto " ) ;
// printf("\tCompare eaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto);
// printf("\tCompare iaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto);
// TODO Check time
// Check that the paquet found in trace correspond to the one we are looking for
if ( /*(strcmp(eaddr, src_addr) == 0) &&*/ ( strcmp ( iaddr , dst_addr ) = = 0 ) & & ( strncmp ( proto_tmp , proto , strlen ( proto_tmp ) ) = = 0 ) )
{
sport = strstr ( buf , " SPT= " ) ;
sport + = 4 ;
dport = strstr ( buf , " DPT= " ) ;
dport + = 4 ;
printf ( " \t Compare eport: %hu \n \t to port: %d \n " , * eport , atoi ( sport ) ) ;
printf ( " \t Compare iport: %hu \n \t to port: %d \n " , * iport , atoi ( dport ) ) ;
if ( /*eport != atoi(sport) &&*/ * iport ! = atoi ( dport ) )
{
printf ( " \t \t Port not corresponding \n " ) ;
continue ;
}
printf ( " \t table found: %.*s \n " , 6 , t ) ;
printf ( " \t chain found: %.*s \n " , 9 , c ) ;
// Check that the table correspond to the filter table
if ( strncmp ( t , " filter " , 6 ) = = 0 )
{
// Check that the table correspond to the MINIUPNP table
if ( strncmp ( c , " MINIUPNPD " , 9 ) = = 0 )
{
* rulenum_used = atoi ( line ) ;
res = 1 ;
}
else
{
res = - 6 ;
continue ;
}
}
else
{
res = - 5 ;
continue ;
}
}
else
{
printf ( " Packet information not corresponding \n " ) ;
continue ;
}
}
if ( ! r & & p )
{
printf ( " \t ** Policy case \n " ) ;
char * src , * dst , * sport , * dport , * proto , * line ;
char time [ 15 ] , src_addr [ 40 ] , dst_addr [ 40 ] , proto_tmp [ 8 ] ;
int proto_int ;
strncpy ( time , buf , sizeof ( time ) ) ;
/*if(compare_time(time, expire_time)<0)
{
printf ( " \t \t Not corresponding time \n " ) ;
continue ;
} */
line = p + 8 ;
printf ( " \t rule line = %d \n " , atoi ( line ) ) ;
src = strstr ( buf , " SRC= " ) ;
src + = 4 ;
snprintf ( src_addr , sizeof ( src_addr ) , " %.*s " , 39 , src ) ;
#if 0
del_char ( src_addr ) ;
add_char ( src_addr ) ;
# endif
dst = strstr ( buf , " DST= " ) ;
dst + = 4 ;
snprintf ( dst_addr , sizeof ( dst_addr ) , " %.*s " , 39 , dst ) ;
#if 0
del_char ( dst_addr ) ;
add_char ( dst_addr ) ;
# endif
proto = strstr ( buf , " PROTO= " ) ;
proto + = 6 ;
proto_int = atoi ( protocol ) ;
if ( proto_int = = IPPROTO_UDP )
strcpy ( proto_tmp , " UDP " ) ;
else if ( proto_int = = IPPROTO_TCP )
strcpy ( proto_tmp , " TCP " ) ;
# ifdef IPPROTO_UDPLITE
else if ( proto_int = = IPPROTO_UDPLITE )
strcpy ( proto_tmp , " UDPLITE " ) ;
# endif
else
strcpy ( proto_tmp , " UnsupportedProto " ) ;
// printf("\tCompare eaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto);
// printf("\tCompare iaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto);
// Check that the paquet found in trace correspond to the one we are looking for
if ( ( strcmp ( eaddr , src_addr ) = = 0 ) & & ( strcmp ( iaddr , dst_addr ) = = 0 ) & & ( strncmp ( proto_tmp , proto , 5 ) = = 0 ) )
{
sport = strstr ( buf , " SPT= " ) ;
sport + = 4 ;
dport = strstr ( buf , " DPT= " ) ;
dport + = 4 ;
printf ( " \t Compare eport: %hu \n \t to port: %d \n " , * eport , atoi ( sport ) ) ;
printf ( " \t Compare iport: %hu \n \t to port: %d \n " , * iport , atoi ( dport ) ) ;
if ( * eport ! = atoi ( sport ) & & * iport ! = atoi ( dport ) )
{
printf ( " \t \t Port not corresponding \n " ) ;
continue ;
}
else
{
printf ( " Find a corresponding policy trace in the chain: %.*s \n " , 10 , c ) ;
res = - 7 ;
continue ;
}
}
else
continue ;
}
}
fclose ( fd ) ;
return res ;
# else
return - 4 ;
# endif
}
int
2012-04-22 00:58:27 +00:00
upnp_clean_expired_pinholes ( unsigned int * next_timestamp )
2011-09-28 19:13:20 +00:00
{
2012-04-22 00:58:27 +00:00
# ifdef USE_PF
return clean_pinhole_list ( next_timestamp ) ;
# else
2011-09-28 19:13:20 +00:00
return 0 ;
# endif
}
# endif
/* stuff for miniupnpdctl */
# ifdef USE_MINIUPNPDCTL
void
write_ruleset_details ( int s )
{
int proto = 0 ;
unsigned short eport , iport ;
char desc [ 64 ] ;
char iaddr [ 32 ] ;
char rhost [ 32 ] ;
unsigned int timestamp ;
u_int64_t packets ;
u_int64_t bytes ;
int i = 0 ;
char buffer [ 256 ] ;
int n ;
write ( s , " Ruleset : \n " , 10 ) ;
while ( get_redirect_rule_by_index ( i , 0 /*ifname*/ , & eport , iaddr , sizeof ( iaddr ) ,
& iport , & proto , desc , sizeof ( desc ) ,
rhost , sizeof ( rhost ) ,
& timestamp ,
& packets , & bytes ) > = 0 )
{
n = snprintf ( buffer , sizeof ( buffer ) ,
" %2d %s %s:%hu->%s:%hu "
" '%s' %u % " PRIu64 " % " PRIu64 " \n " ,
/*"'%s' %llu %llu\n",*/
i , proto = = IPPROTO_TCP ? " TCP " : " UDP " , rhost ,
eport , iaddr , iport , desc , timestamp , packets , bytes ) ;
write ( s , buffer , n ) ;
i + + ;
}
}
# endif