2005-09-26 07:28:27 +02:00
/*
* OpenVPN - - An application to securely tunnel IP networks
* over a single TCP / UDP port , with support for SSL / TLS - based
* session authentication and key exchange ,
* packet encryption , packet authentication , and
* packet compression .
*
* Copyright ( C ) 2002 - 2005 OpenVPN Solutions LLC < info @ openvpn . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ( see the file COPYING included with this
* distribution ) ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/*
* Support routines for configuring and accessing TUN / TAP
* virtual network adapters .
*
* This file is based on the TUN / TAP driver interface routines
* from VTun by Maxim Krasnyansky < max_mk @ yahoo . com > .
*/
# ifdef WIN32
# include "config-win32.h"
# else
# include "config.h"
# endif
# include "syshead.h"
# include "tun.h"
# include "fdmisc.h"
# include "common.h"
# include "misc.h"
# include "socket.h"
# include "manage.h"
2005-12-05 02:21:49 +01:00
# include "route.h"
2005-09-26 07:28:27 +02:00
# include "memdbg.h"
2005-10-31 04:01:17 +01:00
# ifdef WIN32
2005-12-22 19:09:40 +01:00
/* #define SIMULATE_DHCP_FAILED */ /* JYFIXME -- simulate bad DHCP negotiation */
2005-10-31 04:01:17 +01:00
# define NI_TEST_FIRST (1<<0)
# define NI_IP_NETMASK (1<<1)
# define NI_OPTIONS (1<<2)
static void netsh_ifconfig ( const struct tuntap_options * to ,
const char * flex_name ,
const in_addr_t ip ,
const in_addr_t netmask ,
unsigned int flags ) ;
static const char * netsh_get_id ( const char * dev_node , struct gc_arena * gc ) ;
# endif
2005-09-26 07:28:27 +02:00
# ifdef TARGET_SOLARIS
static void solaris_error_close ( struct tuntap * tt , const struct env_set * es , const char * actual ) ;
# endif
bool
is_dev_type ( const char * dev , const char * dev_type , const char * match_type )
{
ASSERT ( match_type ) ;
if ( ! dev )
return false ;
if ( dev_type )
return ! strcmp ( dev_type , match_type ) ;
else
return ! strncmp ( dev , match_type , strlen ( match_type ) ) ;
}
int
dev_type_enum ( const char * dev , const char * dev_type )
{
if ( is_dev_type ( dev , dev_type , " tun " ) )
return DEV_TYPE_TUN ;
else if ( is_dev_type ( dev , dev_type , " tap " ) )
return DEV_TYPE_TAP ;
else if ( is_dev_type ( dev , dev_type , " null " ) )
return DEV_TYPE_NULL ;
else
return DEV_TYPE_UNDEF ;
}
const char *
dev_type_string ( const char * dev , const char * dev_type )
{
switch ( dev_type_enum ( dev , dev_type ) )
{
case DEV_TYPE_TUN :
return " tun " ;
case DEV_TYPE_TAP :
return " tap " ;
case DEV_TYPE_NULL :
return " null " ;
default :
return " [unknown-dev-type] " ;
}
}
const char *
dev_component_in_dev_node ( const char * dev_node )
{
const char * ret ;
const int dirsep = OS_SPECIFIC_DIRSEP ;
if ( dev_node )
{
ret = strrchr ( dev_node , dirsep ) ;
if ( ret & & * ret )
+ + ret ;
else
ret = dev_node ;
if ( * ret )
return ret ;
}
return NULL ;
}
/*
* Try to predict the actual TUN / TAP device instance name ,
* before the device is actually opened .
*/
const char *
guess_tuntap_dev ( const char * dev ,
const char * dev_type ,
const char * dev_node ,
struct gc_arena * gc )
{
# ifdef WIN32
const int dt = dev_type_enum ( dev , dev_type ) ;
if ( dt = = DEV_TYPE_TUN | | dt = = DEV_TYPE_TAP )
{
2005-10-31 04:01:17 +01:00
return netsh_get_id ( dev_node , gc ) ;
2005-09-26 07:28:27 +02:00
}
# endif
/* default case */
return dev ;
}
/*
* Called by the open_tun function of OSes to check if we
* explicitly support IPv6 .
*
* In this context , explicit means that the OS expects us to
* do something special to the tun socket in order to support
* IPv6 , i . e . it is not transparent .
*
* ipv6_explicitly_supported should be set to false if we don ' t
* have any explicit IPv6 code in the tun device handler .
*
* If ipv6_explicitly_supported is true , then we have explicit
* OS - specific tun dev code for handling IPv6 . If so , tt - > ipv6
* is set according to the - - tun - ipv6 command line option .
*/
static void
ipv6_support ( bool ipv6 , bool ipv6_explicitly_supported , struct tuntap * tt )
{
tt - > ipv6 = false ;
if ( ipv6_explicitly_supported )
tt - > ipv6 = ipv6 ;
else if ( ipv6 )
msg ( M_WARN , " NOTE: explicit support for IPv6 tun devices is not provided for this OS " ) ;
}
/* --ifconfig-nowarn disables some options sanity checking */
static const char ifconfig_warn_how_to_silence [ ] = " (silence this warning with --ifconfig-nowarn) " ;
/*
* If ! tun , make sure ifconfig_remote_netmask looks
* like a netmask .
*
* If tun , make sure ifconfig_remote_netmask looks
* like an IPv4 address .
*/
static void
2005-09-26 09:40:02 +02:00
ifconfig_sanity_check ( bool tun , in_addr_t addr , int topology )
2005-09-26 07:28:27 +02:00
{
struct gc_arena gc = gc_new ( ) ;
const bool looks_like_netmask = ( ( addr & 0xFF000000 ) = = 0xFF000000 ) ;
if ( tun )
{
2005-09-26 09:40:02 +02:00
if ( looks_like_netmask & & ( topology = = TOP_NET30 | | topology = = TOP_P2P ) )
msg ( M_WARN , " WARNING: Since you are using --dev tun with a point-to-point topology, the second argument to --ifconfig must be an IP address. You are using something (%s) that looks more like a netmask. %s " ,
2005-09-26 07:28:27 +02:00
print_in_addr_t ( addr , 0 , & gc ) ,
ifconfig_warn_how_to_silence ) ;
}
else /* tap */
{
if ( ! looks_like_netmask )
msg ( M_WARN , " WARNING: Since you are using --dev tap, the second argument to --ifconfig must be a netmask, for example something like 255.255.255.0. %s " ,
ifconfig_warn_how_to_silence ) ;
}
gc_free ( & gc ) ;
}
/*
* For TAP - style devices , generate a broadcast address .
*/
static in_addr_t
generate_ifconfig_broadcast_addr ( in_addr_t local ,
in_addr_t netmask )
{
return local | ~ netmask ;
}
/*
* Check that - - local and - - remote addresses do not
* clash with ifconfig addresses or subnet .
*/
static void
check_addr_clash ( const char * name ,
int type ,
in_addr_t public ,
in_addr_t local ,
in_addr_t remote_netmask )
{
struct gc_arena gc = gc_new ( ) ;
#if 0
msg ( M_INFO , " CHECK_ADDR_CLASH type=%d public=%s local=%s, remote_netmask=%s " ,
type ,
print_in_addr_t ( public , 0 , & gc ) ,
print_in_addr_t ( local , 0 , & gc ) ,
print_in_addr_t ( remote_netmask , 0 , & gc ) ) ;
# endif
if ( public )
{
if ( type = = DEV_TYPE_TUN )
{
const in_addr_t test_netmask = 0xFFFFFF00 ;
const in_addr_t public_net = public & test_netmask ;
const in_addr_t local_net = local & test_netmask ;
const in_addr_t remote_net = remote_netmask & test_netmask ;
if ( public = = local | | public = = remote_netmask )
msg ( M_WARN ,
" WARNING: --%s address [%s] conflicts with --ifconfig address pair [%s, %s]. %s " ,
name ,
print_in_addr_t ( public , 0 , & gc ) ,
print_in_addr_t ( local , 0 , & gc ) ,
print_in_addr_t ( remote_netmask , 0 , & gc ) ,
ifconfig_warn_how_to_silence ) ;
if ( public_net = = local_net | | public_net = = remote_net )
msg ( M_WARN ,
" WARNING: potential conflict between --%s address [%s] and --ifconfig address pair [%s, %s] -- this is a warning only that is triggered when local/remote addresses exist within the same /24 subnet as --ifconfig endpoints. %s " ,
name ,
print_in_addr_t ( public , 0 , & gc ) ,
print_in_addr_t ( local , 0 , & gc ) ,
print_in_addr_t ( remote_netmask , 0 , & gc ) ,
ifconfig_warn_how_to_silence ) ;
}
else if ( type = = DEV_TYPE_TAP )
{
const in_addr_t public_network = public & remote_netmask ;
const in_addr_t virtual_network = local & remote_netmask ;
if ( public_network = = virtual_network )
msg ( M_WARN ,
" WARNING: --%s address [%s] conflicts with --ifconfig subnet [%s, %s] -- local and remote addresses cannot be inside of the --ifconfig subnet. %s " ,
name ,
print_in_addr_t ( public , 0 , & gc ) ,
print_in_addr_t ( local , 0 , & gc ) ,
print_in_addr_t ( remote_netmask , 0 , & gc ) ,
ifconfig_warn_how_to_silence ) ;
}
}
gc_free ( & gc ) ;
}
/*
* Complain if - - dev tap and - - ifconfig is used on an OS for which
* we don ' t have a custom tap ifconfig template below .
*/
static void
no_tap_ifconfig ( )
{
msg ( M_FATAL , " Sorry but you cannot use --dev tap and --ifconfig together on this OS because I have not yet been programmed to understand the appropriate ifconfig syntax to use for TAP-style devices on this OS. Your best alternative is to use an --up script and do the ifconfig command manually. " ) ;
}
/*
* Return a string to be used for options compatibility check
* between peers .
*/
const char *
ifconfig_options_string ( const struct tuntap * tt , bool remote , bool disable , struct gc_arena * gc )
{
struct buffer out = alloc_buf_gc ( 256 , gc ) ;
if ( tt - > did_ifconfig_setup & & ! disable )
{
2005-09-26 09:40:02 +02:00
if ( tt - > type = = DEV_TYPE_TAP | | ( tt - > type = = DEV_TYPE_TUN & & tt - > topology = = TOP_SUBNET ) )
{
buf_printf ( & out , " %s %s " ,
print_in_addr_t ( tt - > local & tt - > remote_netmask , 0 , gc ) ,
print_in_addr_t ( tt - > remote_netmask , 0 , gc ) ) ;
}
else if ( tt - > type = = DEV_TYPE_TUN )
2005-09-26 07:28:27 +02:00
{
const char * l , * r ;
if ( remote )
{
r = print_in_addr_t ( tt - > local , 0 , gc ) ;
l = print_in_addr_t ( tt - > remote_netmask , 0 , gc ) ;
}
else
{
l = print_in_addr_t ( tt - > local , 0 , gc ) ;
r = print_in_addr_t ( tt - > remote_netmask , 0 , gc ) ;
}
buf_printf ( & out , " %s %s " , r , l ) ;
}
else
buf_printf ( & out , " [undef] " ) ;
}
return BSTR ( & out ) ;
}
/*
* Return a status string describing wait state .
*/
const char *
tun_stat ( const struct tuntap * tt , unsigned int rwflags , struct gc_arena * gc )
{
struct buffer out = alloc_buf_gc ( 64 , gc ) ;
if ( tt )
{
if ( rwflags & EVENT_READ )
{
buf_printf ( & out , " T%s " ,
( tt - > rwflags_debug & EVENT_READ ) ? " R " : " r " ) ;
# ifdef WIN32
buf_printf ( & out , " %s " ,
overlapped_io_state_ascii ( & tt - > reads ) ) ;
# endif
}
if ( rwflags & EVENT_WRITE )
{
buf_printf ( & out , " T%s " ,
( tt - > rwflags_debug & EVENT_WRITE ) ? " W " : " w " ) ;
# ifdef WIN32
buf_printf ( & out , " %s " ,
overlapped_io_state_ascii ( & tt - > writes ) ) ;
# endif
}
}
else
{
buf_printf ( & out , " T? " ) ;
}
return BSTR ( & out ) ;
}
2005-09-26 09:40:02 +02:00
/*
* Return true for point - to - point topology , false for subnet topology
*/
bool
is_tun_p2p ( const struct tuntap * tt )
{
bool tun = false ;
if ( tt - > type = = DEV_TYPE_TAP | | ( tt - > type = = DEV_TYPE_TUN & & tt - > topology = = TOP_SUBNET ) )
tun = false ;
else if ( tt - > type = = DEV_TYPE_TUN )
tun = true ;
else
ASSERT ( 0 ) ; /* should have been caught in init_tun */
return tun ;
}
2005-09-26 07:28:27 +02:00
/*
* Init tun / tap object .
*
* Set up tuntap structure for ifconfig ,
* but don ' t execute yet .
*/
struct tuntap *
init_tun ( const char * dev , /* --dev option */
const char * dev_type , /* --dev-type option */
2005-09-26 09:40:02 +02:00
int topology , /* one of the TOP_x values */
2005-09-26 07:28:27 +02:00
const char * ifconfig_local_parm , /* --ifconfig parm 1 */
const char * ifconfig_remote_netmask_parm , /* --ifconfig parm 2 */
in_addr_t local_public ,
in_addr_t remote_public ,
const bool strict_warn ,
struct env_set * es )
{
struct gc_arena gc = gc_new ( ) ;
struct tuntap * tt ;
ALLOC_OBJ ( tt , struct tuntap ) ;
clear_tuntap ( tt ) ;
tt - > type = dev_type_enum ( dev , dev_type ) ;
2005-09-26 09:40:02 +02:00
tt - > topology = topology ;
2005-09-26 07:28:27 +02:00
if ( ifconfig_local_parm & & ifconfig_remote_netmask_parm )
{
bool tun = false ;
const char * ifconfig_local = NULL ;
const char * ifconfig_remote_netmask = NULL ;
const char * ifconfig_broadcast = NULL ;
/*
* We only handle TUN / TAP devices here , not - - dev null devices .
*/
2005-09-26 09:40:02 +02:00
tun = is_tun_p2p ( tt ) ;
2005-09-26 07:28:27 +02:00
/*
* Convert arguments to binary IPv4 addresses .
*/
tt - > local = getaddr (
GETADDR_RESOLVE
| GETADDR_HOST_ORDER
| GETADDR_FATAL_ON_SIGNAL
| GETADDR_FATAL ,
ifconfig_local_parm ,
0 ,
NULL ,
NULL ) ;
tt - > remote_netmask = getaddr (
( tun ? GETADDR_RESOLVE : 0 )
| GETADDR_HOST_ORDER
| GETADDR_FATAL_ON_SIGNAL
| GETADDR_FATAL ,
ifconfig_remote_netmask_parm ,
0 ,
NULL ,
NULL ) ;
/*
* Look for common errors in - - ifconfig parms
*/
if ( strict_warn )
{
2005-09-26 09:40:02 +02:00
ifconfig_sanity_check ( tt - > type = = DEV_TYPE_TUN , tt - > remote_netmask , tt - > topology ) ;
2005-09-26 07:28:27 +02:00
/*
* If local_public or remote_public addresses are defined ,
* make sure they do not clash with our virtual subnet .
*/
check_addr_clash ( " local " ,
tt - > type ,
local_public ,
tt - > local ,
tt - > remote_netmask ) ;
check_addr_clash ( " remote " ,
tt - > type ,
remote_public ,
tt - > local ,
tt - > remote_netmask ) ;
}
/*
* Set ifconfig parameters
*/
ifconfig_local = print_in_addr_t ( tt - > local , 0 , & gc ) ;
ifconfig_remote_netmask = print_in_addr_t ( tt - > remote_netmask , 0 , & gc ) ;
/*
* If TAP - style interface , generate broadcast address .
*/
if ( ! tun )
{
tt - > broadcast = generate_ifconfig_broadcast_addr ( tt - > local , tt - > remote_netmask ) ;
ifconfig_broadcast = print_in_addr_t ( tt - > broadcast , 0 , & gc ) ;
}
/*
* Set environmental variables with ifconfig parameters .
*/
if ( es )
{
setenv_str ( es , " ifconfig_local " , ifconfig_local ) ;
if ( tun )
{
setenv_str ( es , " ifconfig_remote " , ifconfig_remote_netmask ) ;
}
else
{
setenv_str ( es , " ifconfig_netmask " , ifconfig_remote_netmask ) ;
setenv_str ( es , " ifconfig_broadcast " , ifconfig_broadcast ) ;
}
}
tt - > did_ifconfig_setup = true ;
}
gc_free ( & gc ) ;
return tt ;
}
/*
* Platform specific tun initializations
*/
void
init_tun_post ( struct tuntap * tt ,
const struct frame * frame ,
const struct tuntap_options * options )
{
tt - > options = * options ;
# ifdef WIN32
overlapped_io_init ( & tt - > reads , frame , FALSE , true ) ;
overlapped_io_init ( & tt - > writes , frame , TRUE , true ) ;
tt - > rw_handle . read = tt - > reads . overlapped . hEvent ;
tt - > rw_handle . write = tt - > writes . overlapped . hEvent ;
tt - > adapter_index = ~ 0 ;
# endif
}
/* execute the ifconfig command through the shell */
void
do_ifconfig ( struct tuntap * tt ,
const char * actual , /* actual device name */
int tun_mtu ,
const struct env_set * es )
{
struct gc_arena gc = gc_new ( ) ;
if ( tt - > did_ifconfig_setup )
{
bool tun = false ;
const char * ifconfig_local = NULL ;
const char * ifconfig_remote_netmask = NULL ;
const char * ifconfig_broadcast = NULL ;
char command_line [ 256 ] ;
/*
* We only handle TUN / TAP devices here , not - - dev null devices .
*/
2005-09-26 09:40:02 +02:00
tun = is_tun_p2p ( tt ) ;
2005-09-26 07:28:27 +02:00
/*
* Set ifconfig parameters
*/
ifconfig_local = print_in_addr_t ( tt - > local , 0 , & gc ) ;
ifconfig_remote_netmask = print_in_addr_t ( tt - > remote_netmask , 0 , & gc ) ;
/*
* If TAP - style device , generate broadcast address .
*/
if ( ! tun )
ifconfig_broadcast = print_in_addr_t ( tt - > broadcast , 0 , & gc ) ;
# ifdef ENABLE_MANAGEMENT
if ( management )
{
management_set_state ( management ,
OPENVPN_STATE_ASSIGN_IP ,
NULL ,
2005-10-20 21:00:56 +02:00
tt - > local ,
0 ) ;
2005-09-26 07:28:27 +02:00
}
# endif
# if defined(TARGET_LINUX)
# ifdef CONFIG_FEATURE_IPROUTE
/*
* Set the MTU for the device
*/
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IPROUTE_PATH " link set dev %s up mtu %d " ,
actual ,
tun_mtu
) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " Linux ip link set failed " ) ;
if ( tun ) {
/*
* Set the address for the device
*/
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IPROUTE_PATH " addr add dev %s local %s peer %s " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask
) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " Linux ip addr add failed " ) ;
} else {
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IPROUTE_PATH " addr add dev %s %s/%d broadcast %s " ,
actual ,
ifconfig_local ,
count_netmask_bits ( ifconfig_remote_netmask ) ,
ifconfig_broadcast
) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " Linux ip addr add failed " ) ;
}
tt - > did_ifconfig = true ;
# else
if ( tun )
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s pointopoint %s mtu %d " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
else
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu ,
ifconfig_broadcast
) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " Linux ifconfig failed " ) ;
tt - > did_ifconfig = true ;
# endif /*CONFIG_FEATURE_IPROUTE*/
# elif defined(TARGET_SOLARIS)
/* Solaris 2.6 (and 7?) cannot set all parameters in one go...
* example :
* ifconfig tun2 10.2 .0 .2 10.2 .0 .1 mtu 1450 up
* ifconfig tun2 netmask 255.255 .255 .255
*/
if ( tun )
{
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s %s mtu %d up " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
msg ( M_INFO , " %s " , command_line ) ;
if ( ! system_check ( command_line , es , 0 , " Solaris ifconfig phase-1 failed " ) )
solaris_error_close ( tt , es , actual ) ;
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s netmask 255.255.255.255 " ,
actual
) ;
}
else
no_tap_ifconfig ( ) ;
msg ( M_INFO , " %s " , command_line ) ;
if ( ! system_check ( command_line , es , 0 , " Solaris ifconfig phase-2 failed " ) )
solaris_error_close ( tt , es , actual ) ;
tt - > did_ifconfig = true ;
# elif defined(TARGET_OPENBSD)
/*
* OpenBSD tun devices appear to be persistent by default . It seems in order
* to make this work correctly , we need to delete the previous instance
* ( if it exists ) , and re - ifconfig . Let me know if you know a better way .
*/
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s destroy " ,
actual ) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , 0 , NULL ) ;
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s create " ,
actual ) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , 0 , NULL ) ;
msg ( M_INFO , " NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure " ) ;
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if ( tun )
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
else
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s link0 " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu ,
ifconfig_broadcast
) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " OpenBSD ifconfig failed " ) ;
tt - > did_ifconfig = true ;
# elif defined(TARGET_NETBSD)
if ( tun )
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
else
2006-04-05 09:30:04 +02:00
/*
* NetBSD has distinct tun and tap devices
* so we don ' t need the " link0 " extra parameter to specify we want to do
* tunneling at the ethernet level
*/
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu ,
ifconfig_broadcast
) ;
2005-09-26 07:28:27 +02:00
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " NetBSD ifconfig failed " ) ;
tt - > did_ifconfig = true ;
# elif defined(TARGET_DARWIN)
/*
* Darwin ( i . e . Mac OS X ) seems to exhibit similar behaviour to OpenBSD . . .
*/
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s delete " ,
actual ) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , 0 , NULL ) ;
msg ( M_INFO , " NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure " ) ;
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if ( tun )
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
else
2005-12-05 02:21:49 +01:00
{
if ( tt - > topology = = TOP_SUBNET )
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s %s netmask %s mtu %d up " ,
actual ,
ifconfig_local ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
else
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s netmask %s mtu %d up " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
}
2005-09-26 07:28:27 +02:00
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " Mac OS X ifconfig failed " ) ;
tt - > did_ifconfig = true ;
2005-12-05 02:21:49 +01:00
/* Add a network route for the local tun interface */
if ( ! tun & & tt - > topology = = TOP_SUBNET )
{
struct route r ;
CLEAR ( r ) ;
r . defined = true ;
r . network = tt - > local & tt - > remote_netmask ;
r . netmask = tt - > remote_netmask ;
r . gateway = tt - > local ;
add_route ( & r , tt , 0 , es ) ;
}
2005-09-26 07:28:27 +02:00
# elif defined(TARGET_FREEBSD)
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if ( tun )
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
2006-04-05 08:28:19 +02:00
else {
if ( tt - > topology = = TOP_SUBNET )
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s %s %s netmask %s mtu %d up " ,
actual ,
ifconfig_local ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
else
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
2005-09-26 07:28:27 +02:00
IFCONFIG_PATH " %s %s netmask %s mtu %d up " ,
actual ,
ifconfig_local ,
ifconfig_remote_netmask ,
tun_mtu
) ;
2006-04-05 08:28:19 +02:00
}
2005-09-26 07:28:27 +02:00
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , S_FATAL , " FreeBSD ifconfig failed " ) ;
tt - > did_ifconfig = true ;
2006-04-05 08:28:19 +02:00
/* Add a network route for the local tun interface */
if ( ! tun & & tt - > topology = = TOP_SUBNET )
{
struct route r ;
CLEAR ( r ) ;
r . defined = true ;
r . network = tt - > local & tt - > remote_netmask ;
r . netmask = tt - > remote_netmask ;
r . gateway = tt - > local ;
add_route ( & r , tt , 0 , es ) ;
}
2005-09-26 07:28:27 +02:00
# elif defined (WIN32)
{
/*
* Make sure that both ifconfig addresses are part of the
* same .252 subnet .
*/
if ( tun )
{
verify_255_255_255_252 ( tt - > local , tt - > remote_netmask ) ;
tt - > adapter_netmask = ~ 3 ;
}
else
{
tt - > adapter_netmask = tt - > remote_netmask ;
}
switch ( tt - > options . ip_win32_type )
{
case IPW32_SET_MANUAL :
msg ( M_INFO , " ******** NOTE: Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set) " ,
actual ,
ifconfig_local ,
2005-10-31 04:01:17 +01:00
print_in_addr_t ( tt - > adapter_netmask , 0 , & gc ) ) ;
2005-09-26 07:28:27 +02:00
break ;
case IPW32_SET_NETSH :
if ( ! strcmp ( actual , " NULL " ) )
msg ( M_FATAL , " Error: When using --ip-win32 netsh, if you have more than one TAP-Win32 adapter, you must also specify --dev-node " ) ;
2005-10-31 04:01:17 +01:00
netsh_ifconfig ( & tt - > options ,
actual ,
tt - > local ,
tt - > adapter_netmask ,
NI_IP_NETMASK | NI_OPTIONS ) ;
2005-09-26 07:28:27 +02:00
break ;
}
tt - > did_ifconfig = true ;
}
# else
msg ( M_FATAL , " Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script. " ) ;
# endif
}
gc_free ( & gc ) ;
}
void
clear_tuntap ( struct tuntap * tuntap )
{
CLEAR ( * tuntap ) ;
# ifdef WIN32
tuntap - > hand = NULL ;
# else
tuntap - > fd = - 1 ;
# endif
# ifdef TARGET_SOLARIS
tuntap - > ip_fd = - 1 ;
# endif
tuntap - > ipv6 = false ;
}
static void
open_null ( struct tuntap * tt )
{
tt - > actual_name = string_alloc ( " null " , NULL ) ;
}
# ifndef WIN32
static void
open_tun_generic ( const char * dev , const char * dev_type , const char * dev_node ,
bool ipv6 , bool ipv6_explicitly_supported , bool dynamic ,
struct tuntap * tt )
{
char tunname [ 256 ] ;
char dynamic_name [ 256 ] ;
bool dynamic_opened = false ;
ipv6_support ( ipv6 , ipv6_explicitly_supported , tt ) ;
if ( tt - > type = = DEV_TYPE_NULL )
{
open_null ( tt ) ;
}
else
{
/*
* - - dev - node specified , so open an explicit device node
*/
if ( dev_node )
{
openvpn_snprintf ( tunname , sizeof ( tunname ) , " %s " , dev_node ) ;
}
else
{
/*
* dynamic open is indicated by - - dev specified without
* explicit unit number . Try opening / dev / [ dev ] n
* where n = [ 0 , 255 ] .
*/
if ( dynamic & & ! has_digit ( dev ) )
{
int i ;
for ( i = 0 ; i < 256 ; + + i )
{
openvpn_snprintf ( tunname , sizeof ( tunname ) ,
" /dev/%s%d " , dev , i ) ;
openvpn_snprintf ( dynamic_name , sizeof ( dynamic_name ) ,
" %s%d " , dev , i ) ;
if ( ( tt - > fd = open ( tunname , O_RDWR ) ) > 0 )
{
dynamic_opened = true ;
break ;
}
msg ( D_READ_WRITE | M_ERRNO , " Tried opening %s (failed) " , tunname ) ;
}
if ( ! dynamic_opened )
msg ( M_FATAL , " Cannot allocate TUN/TAP dev dynamically " ) ;
}
/*
* explicit unit number specified
*/
else
{
openvpn_snprintf ( tunname , sizeof ( tunname ) , " /dev/%s " , dev ) ;
}
}
if ( ! dynamic_opened )
{
if ( ( tt - > fd = open ( tunname , O_RDWR ) ) < 0 )
msg ( M_ERR , " Cannot open TUN/TAP dev %s " , tunname ) ;
}
set_nonblock ( tt - > fd ) ;
set_cloexec ( tt - > fd ) ; /* don't pass fd to scripts */
msg ( M_INFO , " TUN/TAP device %s opened " , tunname ) ;
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt - > actual_name = string_alloc ( dynamic_opened ? dynamic_name : dev , NULL ) ;
}
}
static void
close_tun_generic ( struct tuntap * tt )
{
if ( tt - > fd > = 0 )
close ( tt - > fd ) ;
if ( tt - > actual_name )
free ( tt - > actual_name ) ;
clear_tuntap ( tt ) ;
}
# endif
# if defined(TARGET_LINUX)
# ifdef HAVE_LINUX_IF_TUN_H /* New driver support */
# ifndef HAVE_LINUX_SOCKIOS_H
# error header file linux / sockios.h required
# endif
# if defined(HAVE_TUN_PI) && defined(HAVE_IPHDR) && defined(HAVE_IOVEC) && defined(ETH_P_IPV6) && defined(ETH_P_IP) && defined(HAVE_READV) && defined(HAVE_WRITEV)
# define LINUX_IPV6 1
/* #warning IPv6 ON */
# else
# define LINUX_IPV6 0
/* #warning IPv6 OFF */
# endif
# if !PEDANTIC
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
struct ifreq ifr ;
/*
* Set tt - > ipv6 to true if
* ( a ) we have the capability of supporting - - tun - ipv6 , and
* ( b ) - - tun - ipv6 was specified .
*/
ipv6_support ( ipv6 , LINUX_IPV6 , tt ) ;
/*
* We handle - - dev null specially , we do not open / dev / null for this .
*/
if ( tt - > type = = DEV_TYPE_NULL )
{
open_null ( tt ) ;
}
else
{
/*
* Process - - dev - node
*/
const char * node = dev_node ;
if ( ! node )
node = " /dev/net/tun " ;
/*
* Open the interface
*/
if ( ( tt - > fd = open ( node , O_RDWR ) ) < 0 )
{
msg ( M_WARN | M_ERRNO , " Note: Cannot open TUN/TAP dev %s " , node ) ;
goto linux_2_2_fallback ;
}
/*
* Process - - tun - ipv6
*/
CLEAR ( ifr ) ;
if ( ! tt - > ipv6 )
ifr . ifr_flags = IFF_NO_PI ;
# if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
ifr . ifr_flags | = IFF_ONE_QUEUE ;
# endif
/*
* Figure out if tun or tap device
*/
if ( tt - > type = = DEV_TYPE_TUN )
{
ifr . ifr_flags | = IFF_TUN ;
}
else if ( tt - > type = = DEV_TYPE_TAP )
{
ifr . ifr_flags | = IFF_TAP ;
}
else
{
msg ( M_FATAL , " I don't recognize device %s as a tun or tap device " ,
dev ) ;
}
/*
* Set an explicit name , if - - dev is not tun or tap
*/
if ( strcmp ( dev , " tun " ) & & strcmp ( dev , " tap " ) )
strncpynt ( ifr . ifr_name , dev , IFNAMSIZ ) ;
/*
* Use special ioctl that configures tun / tap device with the parms
* we set in ifr
*/
if ( ioctl ( tt - > fd , TUNSETIFF , ( void * ) & ifr ) < 0 )
{
msg ( M_WARN | M_ERRNO , " Note: Cannot ioctl TUNSETIFF %s " , dev ) ;
goto linux_2_2_fallback ;
}
msg ( M_INFO , " TUN/TAP device %s opened " , ifr . ifr_name ) ;
/*
* Try making the TX send queue bigger
*/
# if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
{
struct ifreq netifr ;
int ctl_fd ;
if ( ( ctl_fd = socket ( AF_INET , SOCK_DGRAM , 0 ) ) > = 0 )
{
CLEAR ( netifr ) ;
strncpynt ( netifr . ifr_name , ifr . ifr_name , IFNAMSIZ ) ;
netifr . ifr_qlen = tt - > options . txqueuelen ;
if ( ioctl ( ctl_fd , SIOCSIFTXQLEN , ( void * ) & netifr ) > = 0 )
msg ( D_OSBUF , " TUN/TAP TX queue length set to %d " , tt - > options . txqueuelen ) ;
else
msg ( M_WARN | M_ERRNO , " Note: Cannot set tx queue length on %s " , ifr . ifr_name ) ;
close ( ctl_fd ) ;
}
else
{
msg ( M_WARN | M_ERRNO , " Note: Cannot open control socket on %s " , ifr . ifr_name ) ;
}
}
# endif
set_nonblock ( tt - > fd ) ;
set_cloexec ( tt - > fd ) ;
tt - > actual_name = string_alloc ( ifr . ifr_name , NULL ) ;
}
return ;
linux_2_2_fallback :
msg ( M_INFO , " Note: Attempting fallback to kernel 2.2 TUN/TAP interface " ) ;
if ( tt - > fd > = 0 )
{
close ( tt - > fd ) ;
tt - > fd = - 1 ;
}
open_tun_generic ( dev , dev_type , dev_node , ipv6 , false , true , tt ) ;
}
# else
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
ASSERT ( 0 ) ;
}
# endif
# else
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
open_tun_generic ( dev , dev_type , dev_node , ipv6 , false , true , tt ) ;
}
# endif /* HAVE_LINUX_IF_TUN_H */
# ifdef TUNSETPERSIST
void
tuncfg ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , int persist_mode )
{
struct tuntap * tt ;
ALLOC_OBJ ( tt , struct tuntap ) ;
clear_tuntap ( tt ) ;
tt - > type = dev_type_enum ( dev , dev_type ) ;
open_tun ( dev , dev_type , dev_node , ipv6 , tt ) ;
if ( ioctl ( tt - > fd , TUNSETPERSIST , persist_mode ) < 0 )
msg ( M_ERR , " Cannot ioctl TUNSETPERSIST(%d) %s " , persist_mode , dev ) ;
close_tun ( tt ) ;
msg ( M_INFO , " Persist state set to: %s " , ( persist_mode ? " ON " : " OFF " ) ) ;
}
# endif /* TUNSETPERSIST */
void
close_tun ( struct tuntap * tt )
{
if ( tt )
{
close_tun_generic ( tt ) ;
free ( tt ) ;
}
}
int
write_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
# if LINUX_IPV6
if ( tt - > ipv6 )
{
struct tun_pi pi ;
struct iphdr * iph ;
struct iovec vect [ 2 ] ;
int ret ;
iph = ( struct iphdr * ) buf ;
pi . flags = 0 ;
if ( iph - > version = = 6 )
pi . proto = htons ( ETH_P_IPV6 ) ;
else
pi . proto = htons ( ETH_P_IP ) ;
vect [ 0 ] . iov_len = sizeof ( pi ) ;
vect [ 0 ] . iov_base = & pi ;
vect [ 1 ] . iov_len = len ;
vect [ 1 ] . iov_base = buf ;
ret = writev ( tt - > fd , vect , 2 ) ;
return ( ret - sizeof ( pi ) ) ;
}
else
# endif
return write ( tt - > fd , buf , len ) ;
}
int
read_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
# if LINUX_IPV6
if ( tt - > ipv6 )
{
struct iovec vect [ 2 ] ;
struct tun_pi pi ;
int ret ;
vect [ 0 ] . iov_len = sizeof ( pi ) ;
vect [ 0 ] . iov_base = & pi ;
vect [ 1 ] . iov_len = len ;
vect [ 1 ] . iov_base = buf ;
ret = readv ( tt - > fd , vect , 2 ) ;
return ( ret - sizeof ( pi ) ) ;
}
else
# endif
return read ( tt - > fd , buf , len ) ;
}
# elif defined(TARGET_SOLARIS)
# ifndef TUNNEWPPA
# error I need the symbol TUNNEWPPA from net / if_tun.h
# endif
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
int if_fd , muxid , ppa = - 1 ;
struct ifreq ifr ;
const char * ptr ;
const char * ip_node ;
const char * dev_tuntap_type ;
int link_type ;
bool is_tun ;
ipv6_support ( ipv6 , false , tt ) ;
if ( tt - > type = = DEV_TYPE_NULL )
{
open_null ( tt ) ;
return ;
}
if ( tt - > type = = DEV_TYPE_TUN )
{
ip_node = " /dev/udp " ;
if ( ! dev_node )
dev_node = " /dev/tun " ;
dev_tuntap_type = " tun " ;
link_type = I_PLINK ;
is_tun = true ;
}
else if ( tt - > type = = DEV_TYPE_TAP )
{
ip_node = " /dev/ip " ;
if ( ! dev_node )
dev_node = " /dev/tap " ;
dev_tuntap_type = " tap " ;
link_type = I_PLINK ; /* was: I_LINK */
is_tun = false ;
}
else
{
msg ( M_FATAL , " I don't recognize device %s as a tun or tap device " ,
dev ) ;
}
/* get unit number */
if ( * dev )
{
ptr = dev ;
while ( * ptr & & ! isdigit ( ( int ) * ptr ) )
ptr + + ;
ppa = atoi ( ptr ) ;
}
if ( ( tt - > ip_fd = open ( ip_node , O_RDWR , 0 ) ) < 0 )
msg ( M_ERR , " Can't open %s " , ip_node ) ;
if ( ( tt - > fd = open ( dev_node , O_RDWR , 0 ) ) < 0 )
msg ( M_ERR , " Can't open %s " , dev_node ) ;
/* Assign a new PPA and get its unit number. */
if ( ( ppa = ioctl ( tt - > fd , TUNNEWPPA , ppa ) ) < 0 )
msg ( M_ERR , " Can't assign new interface " ) ;
if ( ( if_fd = open ( dev_node , O_RDWR , 0 ) ) < 0 )
msg ( M_ERR , " Can't open %s (2) " , dev_node ) ;
if ( ioctl ( if_fd , I_PUSH , " ip " ) < 0 )
msg ( M_ERR , " Can't push IP module " ) ;
/* Assign ppa according to the unit number returned by tun device */
if ( ioctl ( if_fd , IF_UNITSEL , ( char * ) & ppa ) < 0 )
msg ( M_ERR , " Can't set PPA %d " , ppa ) ;
if ( ( muxid = ioctl ( tt - > ip_fd , link_type , if_fd ) ) < 0 )
msg ( M_ERR , " Can't link %s device to IP " , dev_tuntap_type ) ;
close ( if_fd ) ;
tt - > actual_name = ( char * ) malloc ( 32 ) ;
check_malloc_return ( tt - > actual_name ) ;
openvpn_snprintf ( tt - > actual_name , 32 , " %s%d " , dev_tuntap_type , ppa ) ;
CLEAR ( ifr ) ;
strncpynt ( ifr . ifr_name , tt - > actual_name , sizeof ( ifr . ifr_name ) ) ;
ifr . ifr_ip_muxid = muxid ;
if ( ioctl ( tt - > ip_fd , SIOCSIFMUXID , & ifr ) < 0 )
{
ioctl ( tt - > ip_fd , I_PUNLINK , muxid ) ;
msg ( M_ERR , " Can't set multiplexor id " ) ;
}
set_nonblock ( tt - > fd ) ;
set_cloexec ( tt - > fd ) ;
set_cloexec ( tt - > ip_fd ) ;
msg ( M_INFO , " TUN/TAP device %s opened " , tt - > actual_name ) ;
}
static void
solaris_close_tun ( struct tuntap * tt )
{
if ( tt )
{
if ( tt - > ip_fd > = 0 )
{
struct ifreq ifr ;
CLEAR ( ifr ) ;
strncpynt ( ifr . ifr_name , tt - > actual_name , sizeof ( ifr . ifr_name ) ) ;
if ( ioctl ( tt - > ip_fd , SIOCGIFFLAGS , & ifr ) < 0 )
msg ( M_WARN | M_ERRNO , " Can't get iface flags " ) ;
if ( ioctl ( tt - > ip_fd , SIOCGIFMUXID , & ifr ) < 0 )
msg ( M_WARN | M_ERRNO , " Can't get multiplexor id " ) ;
if ( ioctl ( tt - > ip_fd , I_PUNLINK , ifr . ifr_ip_muxid ) < 0 )
msg ( M_WARN | M_ERRNO , " Can't unlink interface " ) ;
close ( tt - > ip_fd ) ;
tt - > ip_fd = - 1 ;
}
if ( tt - > fd > = 0 )
{
close ( tt - > fd ) ;
tt - > fd = - 1 ;
}
}
}
/*
* Close TUN device .
*/
void
close_tun ( struct tuntap * tt )
{
if ( tt )
{
solaris_close_tun ( tt ) ;
if ( tt - > actual_name )
free ( tt - > actual_name ) ;
clear_tuntap ( tt ) ;
free ( tt ) ;
}
}
static void
solaris_error_close ( struct tuntap * tt , const struct env_set * es , const char * actual )
{
char command_line [ 256 ] ;
openvpn_snprintf ( command_line , sizeof ( command_line ) ,
IFCONFIG_PATH " %s unplumb " ,
actual ) ;
msg ( M_INFO , " %s " , command_line ) ;
system_check ( command_line , es , 0 , " Solaris ifconfig unplumb failed " ) ;
close_tun ( tt ) ;
msg ( M_FATAL , " Solaris ifconfig failed " ) ;
}
int
write_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
struct strbuf sbuf ;
sbuf . len = len ;
sbuf . buf = ( char * ) buf ;
return putmsg ( tt - > fd , NULL , & sbuf , 0 ) > = 0 ? sbuf . len : - 1 ;
}
int
read_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
struct strbuf sbuf ;
int f = 0 ;
sbuf . maxlen = len ;
sbuf . buf = ( char * ) buf ;
return getmsg ( tt - > fd , NULL , & sbuf , & f ) > = 0 ? sbuf . len : - 1 ;
}
# elif defined(TARGET_OPENBSD)
# if !defined(HAVE_READV) || !defined(HAVE_WRITEV)
# error openbsd build requires readv & writev library functions
# endif
/*
* OpenBSD has a slightly incompatible TUN device from
* the rest of the world , in that it prepends a
* uint32 to the beginning of the IP header
* to designate the protocol ( why not just
* look at the version field in the IP header to
* determine v4 or v6 ? ) .
*
* We strip off this field on reads and
* put it back on writes .
*
* I have not tested TAP devices on OpenBSD ,
* but I have conditionalized the special
* TUN handling code described above to
* go away for TAP devices .
*/
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
open_tun_generic ( dev , dev_type , dev_node , ipv6 , true , true , tt ) ;
/* Enable multicast on the interface */
if ( tt - > fd > = 0 )
{
struct tuninfo info ;
if ( ioctl ( tt - > fd , TUNGIFINFO , & info ) < 0 ) {
msg ( M_WARN | M_ERRNO , " Can't get interface info: %s " ,
strerror ( errno ) ) ;
}
info . flags | = IFF_MULTICAST ;
if ( ioctl ( tt - > fd , TUNSIFINFO , & info ) < 0 ) {
msg ( M_WARN | M_ERRNO , " Can't set interface info: %s " ,
strerror ( errno ) ) ;
}
}
}
void
close_tun ( struct tuntap * tt )
{
if ( tt )
{
close_tun_generic ( tt ) ;
free ( tt ) ;
}
}
static inline int
openbsd_modify_read_write_return ( int len )
{
if ( len > 0 )
return len > sizeof ( u_int32_t ) ? len - sizeof ( u_int32_t ) : 0 ;
else
return len ;
}
int
write_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
if ( tt - > type = = DEV_TYPE_TUN )
{
u_int32_t type ;
struct iovec iv [ 2 ] ;
struct ip * iph ;
iph = ( struct ip * ) buf ;
if ( tt - > ipv6 & & iph - > ip_v = = 6 )
type = htonl ( AF_INET6 ) ;
else
type = htonl ( AF_INET ) ;
iv [ 0 ] . iov_base = & type ;
iv [ 0 ] . iov_len = sizeof ( type ) ;
iv [ 1 ] . iov_base = buf ;
iv [ 1 ] . iov_len = len ;
return openbsd_modify_read_write_return ( writev ( tt - > fd , iv , 2 ) ) ;
}
else
return write ( tt - > fd , buf , len ) ;
}
int
read_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
if ( tt - > type = = DEV_TYPE_TUN )
{
u_int32_t type ;
struct iovec iv [ 2 ] ;
iv [ 0 ] . iov_base = & type ;
iv [ 0 ] . iov_len = sizeof ( type ) ;
iv [ 1 ] . iov_base = buf ;
iv [ 1 ] . iov_len = len ;
return openbsd_modify_read_write_return ( readv ( tt - > fd , iv , 2 ) ) ;
}
else
return read ( tt - > fd , buf , len ) ;
}
# elif defined(TARGET_NETBSD)
/*
* NetBSD does not support IPv6 on tun out of the box ,
* but there exists a patch . When this patch is applied ,
* only two things are left to openvpn :
* 1. Activate multicasting ( this has already been done
* before by the kernel , but we make sure that nobody
* has deactivated multicasting inbetween .
* 2. Deactivate " link layer mode " ( otherwise NetBSD
* prepends the address family to the packet , and we
* would run into the same trouble as with OpenBSD .
*/
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
open_tun_generic ( dev , dev_type , dev_node , ipv6 , true , true , tt ) ;
if ( tt - > fd > = 0 )
{
int i = IFF_POINTOPOINT | IFF_MULTICAST ;
ioctl ( tt - > fd , TUNSIFMODE , & i ) ; /* multicast on */
i = 0 ;
ioctl ( tt - > fd , TUNSLMODE , & i ) ; /* link layer mode off */
}
}
void
close_tun ( struct tuntap * tt )
{
if ( tt )
{
close_tun_generic ( tt ) ;
free ( tt ) ;
}
}
int
write_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
return write ( tt - > fd , buf , len ) ;
}
int
read_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
return read ( tt - > fd , buf , len ) ;
}
# elif defined(TARGET_FREEBSD)
static inline int
freebsd_modify_read_write_return ( int len )
{
if ( len > 0 )
return len > sizeof ( u_int32_t ) ? len - sizeof ( u_int32_t ) : 0 ;
else
return len ;
}
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
open_tun_generic ( dev , dev_type , dev_node , ipv6 , true , true , tt ) ;
if ( tt - > fd > = 0 )
{
int i = 0 ;
/* Disable extended modes */
ioctl ( tt - > fd , TUNSLMODE , & i ) ;
i = 1 ;
ioctl ( tt - > fd , TUNSIFHEAD , & i ) ;
}
}
void
close_tun ( struct tuntap * tt )
{
if ( tt )
{
close_tun_generic ( tt ) ;
free ( tt ) ;
}
}
int
write_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
if ( tt - > type = = DEV_TYPE_TUN )
{
u_int32_t type ;
struct iovec iv [ 2 ] ;
struct ip * iph ;
iph = ( struct ip * ) buf ;
if ( tt - > ipv6 & & iph - > ip_v = = 6 )
type = htonl ( AF_INET6 ) ;
else
type = htonl ( AF_INET ) ;
iv [ 0 ] . iov_base = ( char * ) & type ;
iv [ 0 ] . iov_len = sizeof ( type ) ;
iv [ 1 ] . iov_base = buf ;
iv [ 1 ] . iov_len = len ;
return freebsd_modify_read_write_return ( writev ( tt - > fd , iv , 2 ) ) ;
}
else
return write ( tt - > fd , buf , len ) ;
}
int
read_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
if ( tt - > type = = DEV_TYPE_TUN )
{
u_int32_t type ;
struct iovec iv [ 2 ] ;
iv [ 0 ] . iov_base = ( char * ) & type ;
iv [ 0 ] . iov_len = sizeof ( type ) ;
iv [ 1 ] . iov_base = buf ;
iv [ 1 ] . iov_len = len ;
return freebsd_modify_read_write_return ( readv ( tt - > fd , iv , 2 ) ) ;
}
else
return read ( tt - > fd , buf , len ) ;
}
# elif defined(WIN32)
int
tun_read_queue ( struct tuntap * tt , int maxsize )
{
if ( tt - > reads . iostate = = IOSTATE_INITIAL )
{
DWORD len ;
BOOL status ;
int err ;
/* reset buf to its initial state */
tt - > reads . buf = tt - > reads . buf_init ;
len = maxsize ? maxsize : BLEN ( & tt - > reads . buf ) ;
ASSERT ( len < = BLEN ( & tt - > reads . buf ) ) ;
/* the overlapped read will signal this event on I/O completion */
ASSERT ( ResetEvent ( tt - > reads . overlapped . hEvent ) ) ;
status = ReadFile (
tt - > hand ,
BPTR ( & tt - > reads . buf ) ,
len ,
& tt - > reads . size ,
& tt - > reads . overlapped
) ;
if ( status ) /* operation completed immediately? */
{
/* since we got an immediate return, we must signal the event object ourselves */
ASSERT ( SetEvent ( tt - > reads . overlapped . hEvent ) ) ;
tt - > reads . iostate = IOSTATE_IMMEDIATE_RETURN ;
tt - > reads . status = 0 ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Read immediate return [%d,%d] " ,
( int ) len ,
( int ) tt - > reads . size ) ;
}
else
{
err = GetLastError ( ) ;
if ( err = = ERROR_IO_PENDING ) /* operation queued? */
{
tt - > reads . iostate = IOSTATE_QUEUED ;
tt - > reads . status = err ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Read queued [%d] " ,
( int ) len ) ;
}
else /* error occurred */
{
struct gc_arena gc = gc_new ( ) ;
ASSERT ( SetEvent ( tt - > reads . overlapped . hEvent ) ) ;
tt - > reads . iostate = IOSTATE_IMMEDIATE_RETURN ;
tt - > reads . status = err ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Read error [%d] : %s " ,
( int ) len ,
strerror_win32 ( status , & gc ) ) ;
gc_free ( & gc ) ;
}
}
}
return tt - > reads . iostate ;
}
int
tun_write_queue ( struct tuntap * tt , struct buffer * buf )
{
if ( tt - > writes . iostate = = IOSTATE_INITIAL )
{
BOOL status ;
int err ;
/* make a private copy of buf */
tt - > writes . buf = tt - > writes . buf_init ;
tt - > writes . buf . len = 0 ;
ASSERT ( buf_copy ( & tt - > writes . buf , buf ) ) ;
/* the overlapped write will signal this event on I/O completion */
ASSERT ( ResetEvent ( tt - > writes . overlapped . hEvent ) ) ;
status = WriteFile (
tt - > hand ,
BPTR ( & tt - > writes . buf ) ,
BLEN ( & tt - > writes . buf ) ,
& tt - > writes . size ,
& tt - > writes . overlapped
) ;
if ( status ) /* operation completed immediately? */
{
tt - > writes . iostate = IOSTATE_IMMEDIATE_RETURN ;
/* since we got an immediate return, we must signal the event object ourselves */
ASSERT ( SetEvent ( tt - > writes . overlapped . hEvent ) ) ;
tt - > writes . status = 0 ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Write immediate return [%d,%d] " ,
BLEN ( & tt - > writes . buf ) ,
( int ) tt - > writes . size ) ;
}
else
{
err = GetLastError ( ) ;
if ( err = = ERROR_IO_PENDING ) /* operation queued? */
{
tt - > writes . iostate = IOSTATE_QUEUED ;
tt - > writes . status = err ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Write queued [%d] " ,
BLEN ( & tt - > writes . buf ) ) ;
}
else /* error occurred */
{
struct gc_arena gc = gc_new ( ) ;
ASSERT ( SetEvent ( tt - > writes . overlapped . hEvent ) ) ;
tt - > writes . iostate = IOSTATE_IMMEDIATE_RETURN ;
tt - > writes . status = err ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Write error [%d] : %s " ,
BLEN ( & tt - > writes . buf ) ,
strerror_win32 ( err , & gc ) ) ;
gc_free ( & gc ) ;
}
}
}
return tt - > writes . iostate ;
}
int
tun_finalize (
HANDLE h ,
struct overlapped_io * io ,
struct buffer * buf )
{
int ret = - 1 ;
BOOL status ;
switch ( io - > iostate )
{
case IOSTATE_QUEUED :
status = GetOverlappedResult (
h ,
& io - > overlapped ,
& io - > size ,
FALSE
) ;
if ( status )
{
/* successful return for a queued operation */
if ( buf )
* buf = io - > buf ;
ret = io - > size ;
io - > iostate = IOSTATE_INITIAL ;
ASSERT ( ResetEvent ( io - > overlapped . hEvent ) ) ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Completion success [%d] " , ret ) ;
}
else
{
/* error during a queued operation */
ret = - 1 ;
if ( GetLastError ( ) ! = ERROR_IO_INCOMPLETE )
{
/* if no error (i.e. just not finished yet),
then DON ' T execute this code */
io - > iostate = IOSTATE_INITIAL ;
ASSERT ( ResetEvent ( io - > overlapped . hEvent ) ) ;
msg ( D_WIN32_IO | M_ERRNO , " WIN32 I/O: TAP Completion error " ) ;
}
}
break ;
case IOSTATE_IMMEDIATE_RETURN :
io - > iostate = IOSTATE_INITIAL ;
ASSERT ( ResetEvent ( io - > overlapped . hEvent ) ) ;
if ( io - > status )
{
/* error return for a non-queued operation */
SetLastError ( io - > status ) ;
ret = - 1 ;
msg ( D_WIN32_IO | M_ERRNO , " WIN32 I/O: TAP Completion non-queued error " ) ;
}
else
{
/* successful return for a non-queued operation */
if ( buf )
* buf = io - > buf ;
ret = io - > size ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Completion non-queued success [%d] " , ret ) ;
}
break ;
case IOSTATE_INITIAL : /* were we called without proper queueing? */
SetLastError ( ERROR_INVALID_FUNCTION ) ;
ret = - 1 ;
dmsg ( D_WIN32_IO , " WIN32 I/O: TAP Completion BAD STATE " ) ;
break ;
default :
ASSERT ( 0 ) ;
}
if ( buf )
buf - > len = ret ;
return ret ;
}
const struct tap_reg *
get_tap_reg ( struct gc_arena * gc )
{
HKEY adapter_key ;
LONG status ;
DWORD len ;
struct tap_reg * first = NULL ;
struct tap_reg * last = NULL ;
int i = 0 ;
status = RegOpenKeyEx (
HKEY_LOCAL_MACHINE ,
ADAPTER_KEY ,
0 ,
KEY_READ ,
& adapter_key ) ;
if ( status ! = ERROR_SUCCESS )
msg ( M_FATAL , " Error opening registry key: %s " , ADAPTER_KEY ) ;
while ( true )
{
char enum_name [ 256 ] ;
char unit_string [ 256 ] ;
HKEY unit_key ;
char component_id_string [ ] = " ComponentId " ;
char component_id [ 256 ] ;
char net_cfg_instance_id_string [ ] = " NetCfgInstanceId " ;
char net_cfg_instance_id [ 256 ] ;
DWORD data_type ;
len = sizeof ( enum_name ) ;
status = RegEnumKeyEx (
adapter_key ,
i ,
enum_name ,
& len ,
NULL ,
NULL ,
NULL ,
NULL ) ;
if ( status = = ERROR_NO_MORE_ITEMS )
break ;
else if ( status ! = ERROR_SUCCESS )
msg ( M_FATAL , " Error enumerating registry subkeys of key: %s " ,
ADAPTER_KEY ) ;
openvpn_snprintf ( unit_string , sizeof ( unit_string ) , " %s \\ %s " ,
ADAPTER_KEY , enum_name ) ;
status = RegOpenKeyEx (
HKEY_LOCAL_MACHINE ,
unit_string ,
0 ,
KEY_READ ,
& unit_key ) ;
if ( status ! = ERROR_SUCCESS )
dmsg ( D_REGISTRY , " Error opening registry key: %s " , unit_string ) ;
else
{
len = sizeof ( component_id ) ;
status = RegQueryValueEx (
unit_key ,
component_id_string ,
NULL ,
& data_type ,
component_id ,
& len ) ;
if ( status ! = ERROR_SUCCESS | | data_type ! = REG_SZ )
dmsg ( D_REGISTRY , " Error opening registry key: %s \\ %s " ,
unit_string , component_id_string ) ;
else
{
len = sizeof ( net_cfg_instance_id ) ;
status = RegQueryValueEx (
unit_key ,
net_cfg_instance_id_string ,
NULL ,
& data_type ,
net_cfg_instance_id ,
& len ) ;
if ( status = = ERROR_SUCCESS & & data_type = = REG_SZ )
{
if ( ! strcmp ( component_id , TAP_COMPONENT_ID ) )
{
struct tap_reg * reg ;
ALLOC_OBJ_CLEAR_GC ( reg , struct tap_reg , gc ) ;
reg - > guid = string_alloc ( net_cfg_instance_id , gc ) ;
/* link into return list */
if ( ! first )
first = reg ;
if ( last )
last - > next = reg ;
last = reg ;
}
}
}
RegCloseKey ( unit_key ) ;
}
+ + i ;
}
RegCloseKey ( adapter_key ) ;
return first ;
}
const struct panel_reg *
get_panel_reg ( struct gc_arena * gc )
{
LONG status ;
HKEY network_connections_key ;
DWORD len ;
struct panel_reg * first = NULL ;
struct panel_reg * last = NULL ;
int i = 0 ;
status = RegOpenKeyEx (
HKEY_LOCAL_MACHINE ,
NETWORK_CONNECTIONS_KEY ,
0 ,
KEY_READ ,
& network_connections_key ) ;
if ( status ! = ERROR_SUCCESS )
msg ( M_FATAL , " Error opening registry key: %s " , NETWORK_CONNECTIONS_KEY ) ;
while ( true )
{
char enum_name [ 256 ] ;
char connection_string [ 256 ] ;
HKEY connection_key ;
char name_data [ 256 ] ;
DWORD name_type ;
const char name_string [ ] = " Name " ;
len = sizeof ( enum_name ) ;
status = RegEnumKeyEx (
network_connections_key ,
i ,
enum_name ,
& len ,
NULL ,
NULL ,
NULL ,
NULL ) ;
if ( status = = ERROR_NO_MORE_ITEMS )
break ;
else if ( status ! = ERROR_SUCCESS )
msg ( M_FATAL , " Error enumerating registry subkeys of key: %s " ,
NETWORK_CONNECTIONS_KEY ) ;
openvpn_snprintf ( connection_string , sizeof ( connection_string ) ,
" %s \\ %s \\ Connection " ,
NETWORK_CONNECTIONS_KEY , enum_name ) ;
status = RegOpenKeyEx (
HKEY_LOCAL_MACHINE ,
connection_string ,
0 ,
KEY_READ ,
& connection_key ) ;
if ( status ! = ERROR_SUCCESS )
dmsg ( D_REGISTRY , " Error opening registry key: %s " , connection_string ) ;
else
{
len = sizeof ( name_data ) ;
status = RegQueryValueEx (
connection_key ,
name_string ,
NULL ,
& name_type ,
name_data ,
& len ) ;
if ( status ! = ERROR_SUCCESS | | name_type ! = REG_SZ )
dmsg ( D_REGISTRY , " Error opening registry key: %s \\ %s \\ %s " ,
NETWORK_CONNECTIONS_KEY , connection_string , name_string ) ;
else
{
struct panel_reg * reg ;
ALLOC_OBJ_CLEAR_GC ( reg , struct panel_reg , gc ) ;
reg - > name = string_alloc ( name_data , gc ) ;
reg - > guid = string_alloc ( enum_name , gc ) ;
/* link into return list */
if ( ! first )
first = reg ;
if ( last )
last - > next = reg ;
last = reg ;
}
RegCloseKey ( connection_key ) ;
}
+ + i ;
}
RegCloseKey ( network_connections_key ) ;
return first ;
}
/*
* Check that two addresses are part of the same 255.255 .255 .252 subnet .
*/
void
verify_255_255_255_252 ( in_addr_t local , in_addr_t remote )
{
struct gc_arena gc = gc_new ( ) ;
const unsigned int mask = 3 ;
const char * err = NULL ;
if ( local = = remote )
{
err = " must be different " ;
goto error ;
}
if ( ( local & ( ~ mask ) ) ! = ( remote & ( ~ mask ) ) )
{
err = " must exist within the same 255.255.255.252 subnet. This is a limitation of --dev tun when used with the TAP-WIN32 driver " ;
goto error ;
}
if ( ( local & mask ) = = 0
| | ( local & mask ) = = 3
| | ( remote & mask ) = = 0
| | ( remote & mask ) = = 3 )
{
err = " cannot use the first or last address within a given 255.255.255.252 subnet. This is a limitation of --dev tun when used with the TAP-WIN32 driver " ;
goto error ;
}
gc_free ( & gc ) ;
return ;
error :
msg ( M_FATAL , " There is a problem in your selection of --ifconfig endpoints [local=%s, remote=%s]. The local and remote VPN endpoints %s. Try ' " PACKAGE " --show-valid-subnets' option for more info. " ,
print_in_addr_t ( local , 0 , & gc ) ,
print_in_addr_t ( remote , 0 , & gc ) ,
err ) ;
gc_free ( & gc ) ;
}
void show_valid_win32_tun_subnets ( void )
{
int i ;
int col = 0 ;
printf ( " On Windows, point-to-point IP support (i.e. --dev tun) \n " ) ;
printf ( " is emulated by the TAP-Win32 driver. The major limitation \n " ) ;
printf ( " imposed by this approach is that the --ifconfig local and \n " ) ;
printf ( " remote endpoints must be part of the same 255.255.255.252 \n " ) ;
printf ( " subnet. The following list shows examples of endpoint \n " ) ;
printf ( " pairs which satisfy this requirement. Only the final \n " ) ;
printf ( " component of the IP address pairs is at issue. \n \n " ) ;
printf ( " As an example, the following option would be correct: \n " ) ;
printf ( " --ifconfig 10.7.0.5 10.7.0.6 (on host A) \n " ) ;
printf ( " --ifconfig 10.7.0.6 10.7.0.5 (on host B) \n " ) ;
printf ( " because [5,6] is part of the below list. \n \n " ) ;
for ( i = 0 ; i < 256 ; i + = 4 )
{
printf ( " [%3d,%3d] " , i + 1 , i + 2 ) ;
if ( + + col > 4 )
{
col = 0 ;
printf ( " \n " ) ;
}
}
if ( col )
printf ( " \n " ) ;
}
void
show_tap_win32_adapters ( int msglev , int warnlev )
{
struct gc_arena gc = gc_new ( ) ;
bool warn_panel_null = false ;
bool warn_panel_dup = false ;
bool warn_tap_dup = false ;
int links ;
const struct tap_reg * tr ;
const struct tap_reg * tr1 ;
const struct panel_reg * pr ;
const struct tap_reg * tap_reg = get_tap_reg ( & gc ) ;
const struct panel_reg * panel_reg = get_panel_reg ( & gc ) ;
msg ( msglev , " Available TAP-WIN32 adapters [name, GUID]: " ) ;
/* loop through each TAP-Win32 adapter registry entry */
for ( tr = tap_reg ; tr ! = NULL ; tr = tr - > next )
{
links = 0 ;
/* loop through each network connections entry in the control panel */
for ( pr = panel_reg ; pr ! = NULL ; pr = pr - > next )
{
if ( ! strcmp ( tr - > guid , pr - > guid ) )
{
msg ( msglev , " '%s' %s " , pr - > name , tr - > guid ) ;
+ + links ;
}
}
if ( links > 1 )
{
warn_panel_dup = true ;
}
else if ( links = = 0 )
{
/* a TAP adapter exists without a link from the network
connections control panel */
warn_panel_null = true ;
msg ( msglev , " [NULL] %s " , tr - > guid ) ;
}
}
/* check for TAP-Win32 adapter duplicated GUIDs */
for ( tr = tap_reg ; tr ! = NULL ; tr = tr - > next )
{
for ( tr1 = tap_reg ; tr1 ! = NULL ; tr1 = tr1 - > next )
{
if ( tr ! = tr1 & & ! strcmp ( tr - > guid , tr1 - > guid ) )
warn_tap_dup = true ;
}
}
/* warn on registry inconsistencies */
if ( warn_tap_dup )
msg ( warnlev , " WARNING: Some TAP-Win32 adapters have duplicate GUIDs " ) ;
if ( warn_panel_dup )
msg ( warnlev , " WARNING: Some TAP-Win32 adapters have duplicate links from the Network Connections control panel " ) ;
if ( warn_panel_null )
msg ( warnlev , " WARNING: Some TAP-Win32 adapters have no link from the Network Connections control panel " ) ;
gc_free ( & gc ) ;
}
/*
* Confirm that GUID is a TAP - Win32 adapter .
*/
static bool
is_tap_win32 ( const char * guid , const struct tap_reg * tap_reg )
{
const struct tap_reg * tr ;
for ( tr = tap_reg ; tr ! = NULL ; tr = tr - > next )
{
if ( guid & & ! strcmp ( tr - > guid , guid ) )
return true ;
}
return false ;
}
static const char *
guid_to_name ( const char * guid , const struct panel_reg * panel_reg )
{
const struct panel_reg * pr ;
for ( pr = panel_reg ; pr ! = NULL ; pr = pr - > next )
{
if ( guid & & ! strcmp ( pr - > guid , guid ) )
return pr - > name ;
}
return NULL ;
}
static const char *
name_to_guid ( const char * name , const struct tap_reg * tap_reg , const struct panel_reg * panel_reg )
{
const struct panel_reg * pr ;
for ( pr = panel_reg ; pr ! = NULL ; pr = pr - > next )
{
if ( name & & ! strcmp ( pr - > name , name ) & & is_tap_win32 ( pr - > guid , tap_reg ) )
return pr - > guid ;
}
return NULL ;
}
static void
at_least_one_tap_win32 ( const struct tap_reg * tap_reg )
{
if ( ! tap_reg )
msg ( M_FATAL , " There are no TAP-Win32 adapters on this system. You should be able to create a TAP-Win32 adapter by going to Start -> All Programs -> " PACKAGE_NAME " -> Add a new TAP-Win32 virtual ethernet adapter. " ) ;
}
/*
* Get an adapter GUID and optional actual_name from the
* registry for the TAP device # = device_number .
*/
static const char *
get_unspecified_device_guid ( const int device_number ,
char * actual_name ,
int actual_name_size ,
const struct tap_reg * tap_reg_src ,
const struct panel_reg * panel_reg_src ,
struct gc_arena * gc )
{
const struct tap_reg * tap_reg = tap_reg_src ;
struct buffer ret = clear_buf ( ) ;
struct buffer actual = clear_buf ( ) ;
int i ;
ASSERT ( device_number > = 0 ) ;
/* Make sure we have at least one TAP adapter */
if ( ! tap_reg )
return NULL ;
/* The actual_name output buffer may be NULL */
if ( actual_name )
{
ASSERT ( actual_name_size > 0 ) ;
buf_set_write ( & actual , actual_name , actual_name_size ) ;
}
/* Move on to specified device number */
for ( i = 0 ; i < device_number ; i + + )
{
tap_reg = tap_reg - > next ;
if ( ! tap_reg )
return NULL ;
}
/* Save Network Panel name (if exists) in actual_name */
if ( actual_name )
{
const char * act = guid_to_name ( tap_reg - > guid , panel_reg_src ) ;
if ( act )
buf_printf ( & actual , " %s " , act ) ;
else
2005-10-31 04:01:17 +01:00
buf_printf ( & actual , " %s " , tap_reg - > guid ) ;
2005-09-26 07:28:27 +02:00
}
/* Save GUID for return value */
ret = alloc_buf_gc ( 256 , gc ) ;
buf_printf ( & ret , " %s " , tap_reg - > guid ) ;
return BSTR ( & ret ) ;
}
/*
* Lookup a - - dev - node adapter name in the registry
* returning the GUID and optional actual_name .
*/
static const char *
get_device_guid ( const char * name ,
char * actual_name ,
int actual_name_size ,
const struct tap_reg * tap_reg ,
const struct panel_reg * panel_reg ,
struct gc_arena * gc )
{
struct buffer ret = alloc_buf_gc ( 256 , gc ) ;
struct buffer actual = clear_buf ( ) ;
/* Make sure we have at least one TAP adapter */
if ( ! tap_reg )
return NULL ;
/* The actual_name output buffer may be NULL */
if ( actual_name )
{
ASSERT ( actual_name_size > 0 ) ;
buf_set_write ( & actual , actual_name , actual_name_size ) ;
}
/* Check if GUID was explicitly specified as --dev-node parameter */
if ( is_tap_win32 ( name , tap_reg ) )
{
const char * act = guid_to_name ( name , panel_reg ) ;
buf_printf ( & ret , " %s " , name ) ;
if ( act )
buf_printf ( & actual , " %s " , act ) ;
else
2005-10-31 04:01:17 +01:00
buf_printf ( & actual , " %s " , name ) ;
2005-09-26 07:28:27 +02:00
return BSTR ( & ret ) ;
}
/* Lookup TAP adapter in network connections list */
{
const char * guid = name_to_guid ( name , tap_reg , panel_reg ) ;
if ( guid )
{
buf_printf ( & actual , " %s " , name ) ;
buf_printf ( & ret , " %s " , guid ) ;
return BSTR ( & ret ) ;
}
}
return NULL ;
}
/*
* Get adapter info list
*/
const IP_ADAPTER_INFO *
get_adapter_info_list ( struct gc_arena * gc )
{
ULONG size = 0 ;
IP_ADAPTER_INFO * pi = NULL ;
DWORD status ;
if ( ( status = GetAdaptersInfo ( NULL , & size ) ) ! = ERROR_BUFFER_OVERFLOW )
{
msg ( M_INFO , " GetAdaptersInfo #1 failed (status=%u) : %s " ,
( unsigned int ) status ,
strerror_win32 ( status , gc ) ) ;
}
else
{
pi = ( PIP_ADAPTER_INFO ) gc_malloc ( size , false , gc ) ;
if ( ( status = GetAdaptersInfo ( pi , & size ) ) = = NO_ERROR )
return pi ;
else
{
msg ( M_INFO , " GetAdaptersInfo #2 failed (status=%u) : %s " ,
( unsigned int ) status ,
strerror_win32 ( status , gc ) ) ;
}
}
return pi ;
}
2005-09-26 09:40:02 +02:00
const IP_PER_ADAPTER_INFO *
get_per_adapter_info ( const DWORD index , struct gc_arena * gc )
{
ULONG size = 0 ;
IP_PER_ADAPTER_INFO * pi = NULL ;
DWORD status ;
2005-10-31 04:01:17 +01:00
if ( index ! = ~ 0 )
2005-09-26 09:40:02 +02:00
{
2005-10-31 04:01:17 +01:00
if ( ( status = GetPerAdapterInfo ( index , NULL , & size ) ) ! = ERROR_BUFFER_OVERFLOW )
2005-09-26 09:40:02 +02:00
{
2005-10-31 04:01:17 +01:00
msg ( M_INFO , " GetPerAdapterInfo #1 failed (status=%u) : %s " ,
2005-09-26 09:40:02 +02:00
( unsigned int ) status ,
strerror_win32 ( status , gc ) ) ;
}
2005-10-31 04:01:17 +01:00
else
{
pi = ( PIP_PER_ADAPTER_INFO ) gc_malloc ( size , false , gc ) ;
if ( ( status = GetPerAdapterInfo ( ( ULONG ) index , pi , & size ) ) = = ERROR_SUCCESS )
return pi ;
else
{
msg ( M_INFO , " GetPerAdapterInfo #2 failed (status=%u) : %s " ,
( unsigned int ) status ,
strerror_win32 ( status , gc ) ) ;
}
}
2005-09-26 09:40:02 +02:00
}
return pi ;
}
2005-09-26 07:28:27 +02:00
static const IP_INTERFACE_INFO *
get_interface_info_list ( struct gc_arena * gc )
{
ULONG size = 0 ;
IP_INTERFACE_INFO * ii = NULL ;
DWORD status ;
if ( ( status = GetInterfaceInfo ( NULL , & size ) ) ! = ERROR_INSUFFICIENT_BUFFER )
{
msg ( M_INFO , " GetInterfaceInfo #1 failed (status=%u) : %s " ,
( unsigned int ) status ,
strerror_win32 ( status , gc ) ) ;
}
else
{
ii = ( PIP_INTERFACE_INFO ) gc_malloc ( size , false , gc ) ;
if ( ( status = GetInterfaceInfo ( ii , & size ) ) = = NO_ERROR )
return ii ;
else
{
msg ( M_INFO , " GetInterfaceInfo #2 failed (status=%u) : %s " ,
( unsigned int ) status ,
strerror_win32 ( status , gc ) ) ;
}
}
return ii ;
}
static const IP_ADAPTER_INDEX_MAP *
get_interface_info ( DWORD index , struct gc_arena * gc )
{
const IP_INTERFACE_INFO * list = get_interface_info_list ( gc ) ;
if ( list )
{
int i ;
for ( i = 0 ; i < list - > NumAdapters ; + + i )
{
const IP_ADAPTER_INDEX_MAP * inter = & list - > Adapter [ i ] ;
if ( index = = inter - > Index )
return inter ;
}
}
return NULL ;
}
/*
* Given an adapter index , return a pointer to the
* IP_ADAPTER_INFO structure for that adapter .
*/
static const IP_ADAPTER_INFO *
get_adapter ( const IP_ADAPTER_INFO * ai , DWORD index )
{
if ( ai & & index ! = ( DWORD ) ~ 0 )
{
const IP_ADAPTER_INFO * a ;
/* find index in the linked list */
for ( a = ai ; a ! = NULL ; a = a - > Next )
{
if ( a - > Index = = index )
return a ;
}
}
return NULL ;
}
2005-09-26 09:40:02 +02:00
const IP_ADAPTER_INFO *
2005-09-26 07:28:27 +02:00
get_adapter_info ( DWORD index , struct gc_arena * gc )
{
return get_adapter ( get_adapter_info_list ( gc ) , index ) ;
}
static int
get_adapter_n_ip_netmask ( const IP_ADAPTER_INFO * ai )
{
if ( ai )
{
int n = 0 ;
const IP_ADDR_STRING * ip = & ai - > IpAddressList ;
while ( ip )
{
+ + n ;
ip = ip - > Next ;
}
return n ;
}
else
return 0 ;
}
static bool
get_adapter_ip_netmask ( const IP_ADAPTER_INFO * ai , const int n , in_addr_t * ip , in_addr_t * netmask )
{
bool ret = false ;
* ip = 0 ;
* netmask = 0 ;
if ( ai )
{
const IP_ADDR_STRING * iplist = & ai - > IpAddressList ;
int i = 0 ;
while ( iplist )
{
if ( i = = n )
break ;
+ + i ;
iplist = iplist - > Next ;
}
if ( iplist )
{
const unsigned int getaddr_flags = GETADDR_HOST_ORDER ;
const char * ip_str = iplist - > IpAddress . String ;
const char * netmask_str = iplist - > IpMask . String ;
bool succeed1 = false ;
bool succeed2 = false ;
if ( ip_str & & netmask_str & & strlen ( ip_str ) & & strlen ( netmask_str ) )
{
* ip = getaddr ( getaddr_flags , ip_str , 0 , & succeed1 , NULL ) ;
* netmask = getaddr ( getaddr_flags , netmask_str , 0 , & succeed2 , NULL ) ;
ret = ( succeed1 = = true & & succeed2 = = true ) ;
}
}
}
return ret ;
}
2005-10-31 04:01:17 +01:00
static bool
test_adapter_ip_netmask ( const IP_ADAPTER_INFO * ai , const in_addr_t ip , const in_addr_t netmask )
{
if ( ai )
{
in_addr_t ip_adapter = 0 ;
in_addr_t netmask_adapter = 0 ;
const bool status = get_adapter_ip_netmask ( ai , 0 , & ip_adapter , & netmask_adapter ) ;
return ( status & & ip_adapter = = ip & & netmask_adapter = = netmask ) ;
}
else
return false ;
}
2005-09-26 07:28:27 +02:00
const IP_ADAPTER_INFO *
get_tun_adapter ( const struct tuntap * tt , const IP_ADAPTER_INFO * list )
{
if ( list & & tt )
return get_adapter ( list , tt - > adapter_index ) ;
else
return NULL ;
}
bool
is_adapter_up ( const struct tuntap * tt , const IP_ADAPTER_INFO * list )
{
int i ;
bool ret = false ;
const IP_ADAPTER_INFO * ai = get_tun_adapter ( tt , list ) ;
if ( ai )
{
const int n = get_adapter_n_ip_netmask ( ai ) ;
/* loop once for every IP/netmask assigned to adapter */
for ( i = 0 ; i < n ; + + i )
{
in_addr_t ip , netmask ;
if ( get_adapter_ip_netmask ( ai , i , & ip , & netmask ) )
{
if ( tt - > local & & tt - > adapter_netmask )
{
/* wait for our --ifconfig parms to match the actual adapter parms */
if ( tt - > local = = ip & & tt - > adapter_netmask = = netmask )
ret = true ;
}
else
{
/* --ifconfig was not defined, maybe using a real DHCP server */
if ( ip & & netmask )
ret = true ;
}
}
}
}
else
ret = true ; /* this can occur when TAP adapter is bridged */
return ret ;
}
bool
is_ip_in_adapter_subnet ( const IP_ADAPTER_INFO * ai , const in_addr_t ip , in_addr_t * highest_netmask )
{
int i ;
bool ret = false ;
if ( highest_netmask )
* highest_netmask = 0 ;
if ( ai )
{
const int n = get_adapter_n_ip_netmask ( ai ) ;
for ( i = 0 ; i < n ; + + i )
{
in_addr_t adapter_ip , adapter_netmask ;
if ( get_adapter_ip_netmask ( ai , i , & adapter_ip , & adapter_netmask ) )
{
if ( adapter_ip & & adapter_netmask & & ( ip & adapter_netmask ) = = ( adapter_ip & adapter_netmask ) )
{
if ( highest_netmask & & adapter_netmask > * highest_netmask )
* highest_netmask = adapter_netmask ;
ret = true ;
}
}
}
}
return ret ;
}
DWORD
adapter_index_of_ip ( const IP_ADAPTER_INFO * list , const in_addr_t ip , int * count )
{
struct gc_arena gc = gc_new ( ) ;
DWORD ret = ~ 0 ;
in_addr_t highest_netmask = 0 ;
bool first = true ;
if ( count )
* count = 0 ;
while ( list )
{
in_addr_t hn ;
if ( is_ip_in_adapter_subnet ( list , ip , & hn ) )
{
if ( first | | hn > highest_netmask )
{
highest_netmask = hn ;
if ( count )
* count = 1 ;
ret = list - > Index ;
first = false ;
}
else if ( hn = = highest_netmask )
{
if ( count )
+ + * count ;
}
}
list = list - > Next ;
}
dmsg ( D_ROUTE_DEBUG , " DEBUG: IP Locate: ip=%s nm=%s index=%d count=%d " ,
print_in_addr_t ( ip , 0 , & gc ) ,
print_in_addr_t ( highest_netmask , 0 , & gc ) ,
( int ) ret ,
count ? * count : - 1 ) ;
if ( ret = = ~ 0 & & count )
* count = 0 ;
gc_free ( & gc ) ;
return ret ;
}
/*
* Given an adapter index , return true if the adapter
* is DHCP disabled .
*/
2005-10-31 04:01:17 +01:00
# define DHCP_STATUS_UNDEF 0
# define DHCP_STATUS_ENABLED 1
# define DHCP_STATUS_DISABLED 2
static int
dhcp_status ( DWORD index )
2005-09-26 07:28:27 +02:00
{
struct gc_arena gc = gc_new ( ) ;
2005-10-31 04:01:17 +01:00
int ret = DHCP_STATUS_UNDEF ;
if ( index ! = ~ 0 )
{
const IP_ADAPTER_INFO * ai = get_adapter_info ( index , & gc ) ;
2005-09-26 07:28:27 +02:00
2005-10-31 04:01:17 +01:00
if ( ai )
{
if ( ai - > DhcpEnabled )
ret = DHCP_STATUS_ENABLED ;
else
ret = DHCP_STATUS_DISABLED ;
}
}
2005-09-26 07:28:27 +02:00
gc_free ( & gc ) ;
return ret ;
}
/*
* Delete all temporary address / netmask pairs which were added
* to adapter ( given by index ) by previous calls to AddIPAddress .
*/
static void
delete_temp_addresses ( DWORD index )
{
struct gc_arena gc = gc_new ( ) ;
const IP_ADAPTER_INFO * a = get_adapter_info ( index , & gc ) ;
if ( a )
{
const IP_ADDR_STRING * ip = & a - > IpAddressList ;
while ( ip )
{
DWORD status ;
const DWORD context = ip - > Context ;
if ( ( status = DeleteIPAddress ( ( ULONG ) context ) ) = = NO_ERROR )
{
msg ( M_INFO , " Successfully deleted previously set dynamic IP/netmask: %s/%s " ,
ip - > IpAddress . String ,
ip - > IpMask . String ) ;
}
else
{
const char * empty = " 0.0.0.0 " ;
if ( strcmp ( ip - > IpAddress . String , empty )
| | strcmp ( ip - > IpMask . String , empty ) )
msg ( M_INFO , " NOTE: could not delete previously set dynamic IP/netmask: %s/%s (status=%u) " ,
ip - > IpAddress . String ,
ip - > IpMask . String ,
( unsigned int ) status ) ;
}
ip = ip - > Next ;
}
}
gc_free ( & gc ) ;
}
/*
* Get interface index for use with IP Helper API functions .
*/
static DWORD
2005-10-31 04:01:17 +01:00
get_adapter_index_method_1 ( const char * guid )
2005-09-26 07:28:27 +02:00
{
struct gc_arena gc = gc_new ( ) ;
2005-10-31 04:01:17 +01:00
ULONG index = ~ 0 ;
2005-09-26 07:28:27 +02:00
DWORD status ;
wchar_t wbuf [ 256 ] ;
snwprintf ( wbuf , SIZE ( wbuf ) , L " \\ DEVICE \\ TCPIP_%S " , guid ) ;
wbuf [ SIZE ( wbuf ) - 1 ] = 0 ;
if ( ( status = GetAdapterIndex ( wbuf , & index ) ) ! = NO_ERROR )
2005-10-31 04:01:17 +01:00
index = ~ 0 ;
gc_free ( & gc ) ;
return index ;
}
static DWORD
get_adapter_index_method_2 ( const char * guid )
{
struct gc_arena gc = gc_new ( ) ;
DWORD index = ~ 0 ;
const IP_ADAPTER_INFO * list = get_adapter_info_list ( & gc ) ;
while ( list )
2005-09-26 07:28:27 +02:00
{
2005-10-31 04:01:17 +01:00
if ( ! strcmp ( guid , list - > AdapterName ) )
{
index = list - > Index ;
break ;
}
list = list - > Next ;
2005-09-26 07:28:27 +02:00
}
2005-10-31 04:01:17 +01:00
gc_free ( & gc ) ;
return index ;
}
static DWORD
get_adapter_index ( const char * guid )
{
DWORD index ;
index = get_adapter_index_method_1 ( guid ) ;
if ( index = = ~ 0 )
index = get_adapter_index_method_2 ( guid ) ;
if ( index = = ~ 0 )
msg ( M_INFO , " NOTE: could not get adapter index for %s " , guid ) ;
return index ;
}
static DWORD
get_adapter_index_flexible ( const char * name ) /* actual name or GUID */
{
struct gc_arena gc = gc_new ( ) ;
DWORD index ;
index = get_adapter_index_method_1 ( name ) ;
if ( index = = ~ 0 )
index = get_adapter_index_method_2 ( name ) ;
if ( index = = ~ 0 )
{
const struct tap_reg * tap_reg = get_tap_reg ( & gc ) ;
const struct panel_reg * panel_reg = get_panel_reg ( & gc ) ;
const char * guid = name_to_guid ( name , tap_reg , panel_reg ) ;
index = get_adapter_index_method_1 ( guid ) ;
if ( index = = ~ 0 )
index = get_adapter_index_method_2 ( guid ) ;
}
if ( index = = ~ 0 )
msg ( M_INFO , " NOTE: could not get adapter index for name/GUID '%s' " , name ) ;
gc_free ( & gc ) ;
return index ;
2005-09-26 07:28:27 +02:00
}
/*
* Return a string representing a PIP_ADDR_STRING
*/
static const char *
format_ip_addr_string ( const IP_ADDR_STRING * ip , struct gc_arena * gc )
{
struct buffer out = alloc_buf_gc ( 256 , gc ) ;
while ( ip )
{
buf_printf ( & out , " %s " , ip - > IpAddress . String ) ;
if ( strlen ( ip - > IpMask . String ) )
{
buf_printf ( & out , " / " ) ;
buf_printf ( & out , " %s " , ip - > IpMask . String ) ;
}
buf_printf ( & out , " " ) ;
ip = ip - > Next ;
}
return BSTR ( & out ) ;
}
/*
* Show info for a single adapter
*/
static void
show_adapter ( int msglev , const IP_ADAPTER_INFO * a , struct gc_arena * gc )
{
msg ( msglev , " %s " , a - > Description ) ;
msg ( msglev , " Index = %d " , ( int ) a - > Index ) ;
msg ( msglev , " GUID = %s " , a - > AdapterName ) ;
msg ( msglev , " IP = %s " , format_ip_addr_string ( & a - > IpAddressList , gc ) ) ;
msg ( msglev , " MAC = %s " , format_hex_ex ( a - > Address , a - > AddressLength , 0 , 1 , " : " , gc ) ) ;
msg ( msglev , " GATEWAY = %s " , format_ip_addr_string ( & a - > GatewayList , gc ) ) ;
if ( a - > DhcpEnabled )
{
msg ( msglev , " DHCP SERV = %s " , format_ip_addr_string ( & a - > DhcpServer , gc ) ) ;
msg ( msglev , " DHCP LEASE OBTAINED = %s " , time_string ( a - > LeaseObtained , 0 , false , gc ) ) ;
msg ( msglev , " DHCP LEASE EXPIRES = %s " , time_string ( a - > LeaseExpires , 0 , false , gc ) ) ;
}
if ( a - > HaveWins )
{
msg ( msglev , " PRI WINS = %s " , format_ip_addr_string ( & a - > PrimaryWinsServer , gc ) ) ;
msg ( msglev , " SEC WINS = %s " , format_ip_addr_string ( & a - > SecondaryWinsServer , gc ) ) ;
}
2005-09-26 09:40:02 +02:00
{
const IP_PER_ADAPTER_INFO * pai = get_per_adapter_info ( a - > Index , gc ) ;
if ( pai )
{
msg ( msglev , " DNS SERV = %s " , format_ip_addr_string ( & pai - > DnsServerList , gc ) ) ;
}
}
2005-09-26 07:28:27 +02:00
}
/*
* Show current adapter list
*/
void
show_adapters ( int msglev )
{
struct gc_arena gc = gc_new ( ) ;
const IP_ADAPTER_INFO * ai = get_adapter_info_list ( & gc ) ;
msg ( msglev , " SYSTEM ADAPTER LIST " ) ;
if ( ai )
{
const IP_ADAPTER_INFO * a ;
/* find index in the linked list */
for ( a = ai ; a ! = NULL ; a = a - > Next )
{
show_adapter ( msglev , a , & gc ) ;
}
}
gc_free ( & gc ) ;
}
2005-09-26 09:40:02 +02:00
/*
* Set a particular TAP - Win32 adapter ( or all of them if
* adapter_name = = NULL ) to allow it to be opened from
* a non - admin account . This setting will only persist
* for the lifetime of the device object .
*/
static void
tap_allow_nonadmin_access_handle ( const char * device_path , HANDLE hand )
{
struct security_attributes sa ;
BOOL status ;
if ( ! init_security_attributes_allow_all ( & sa ) )
msg ( M_ERR , " Error: init SA failed " ) ;
status = SetKernelObjectSecurity ( hand , DACL_SECURITY_INFORMATION , & sa . sd ) ;
if ( ! status )
{
msg ( M_ERRNO , " Error: SetKernelObjectSecurity failed on %s " , device_path ) ;
}
else
{
msg ( M_INFO | M_NOPREFIX , " TAP-Win32 device: %s [Non-admin access allowed] " , device_path ) ;
}
}
void
tap_allow_nonadmin_access ( const char * dev_node )
{
struct gc_arena gc = gc_new ( ) ;
const struct tap_reg * tap_reg = get_tap_reg ( & gc ) ;
const struct panel_reg * panel_reg = get_panel_reg ( & gc ) ;
const char * device_guid = NULL ;
HANDLE hand ;
2005-10-31 04:01:17 +01:00
char actual_buffer [ 256 ] ;
2005-09-26 09:40:02 +02:00
char device_path [ 256 ] ;
at_least_one_tap_win32 ( tap_reg ) ;
if ( dev_node )
{
/* Get the device GUID for the device specified with --dev-node. */
2005-10-31 04:01:17 +01:00
device_guid = get_device_guid ( dev_node , actual_buffer , sizeof ( actual_buffer ) , tap_reg , panel_reg , & gc ) ;
2005-09-26 09:40:02 +02:00
if ( ! device_guid )
msg ( M_FATAL , " TAP-Win32 adapter '%s' not found " , dev_node ) ;
/* Open Windows TAP-Win32 adapter */
openvpn_snprintf ( device_path , sizeof ( device_path ) , " %s%s%s " ,
USERMODEDEVICEDIR ,
device_guid ,
TAPSUFFIX ) ;
hand = CreateFile (
device_path ,
MAXIMUM_ALLOWED ,
0 , /* was: FILE_SHARE_READ */
0 ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED ,
0
) ;
if ( hand = = INVALID_HANDLE_VALUE )
msg ( M_ERR , " CreateFile failed on TAP device: %s " , device_path ) ;
tap_allow_nonadmin_access_handle ( device_path , hand ) ;
CloseHandle ( hand ) ;
}
else
{
int device_number = 0 ;
/* Try opening all TAP devices */
while ( true )
{
device_guid = get_unspecified_device_guid ( device_number ,
2005-10-31 04:01:17 +01:00
actual_buffer ,
sizeof ( actual_buffer ) ,
2005-09-26 09:40:02 +02:00
tap_reg ,
panel_reg ,
& gc ) ;
if ( ! device_guid )
break ;
/* Open Windows TAP-Win32 adapter */
openvpn_snprintf ( device_path , sizeof ( device_path ) , " %s%s%s " ,
USERMODEDEVICEDIR ,
device_guid ,
TAPSUFFIX ) ;
hand = CreateFile (
device_path ,
MAXIMUM_ALLOWED ,
0 , /* was: FILE_SHARE_READ */
0 ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED ,
0
) ;
if ( hand = = INVALID_HANDLE_VALUE )
msg ( M_WARN , " CreateFile failed on TAP device: %s " , device_path ) ;
else
{
tap_allow_nonadmin_access_handle ( device_path , hand ) ;
CloseHandle ( hand ) ;
}
device_number + + ;
}
}
gc_free ( & gc ) ;
}
2005-09-26 07:28:27 +02:00
/*
* DHCP release / renewal
*/
bool
dhcp_release ( const struct tuntap * tt )
{
struct gc_arena gc = gc_new ( ) ;
bool ret = false ;
if ( tt & & tt - > options . ip_win32_type = = IPW32_SET_DHCP_MASQ & & tt - > adapter_index ! = ~ 0 )
{
const IP_ADAPTER_INDEX_MAP * inter = get_interface_info ( tt - > adapter_index , & gc ) ;
if ( inter )
{
DWORD status = IpReleaseAddress ( ( IP_ADAPTER_INDEX_MAP * ) inter ) ;
if ( status = = NO_ERROR )
{
msg ( D_TUNTAP_INFO , " TAP: DHCP address released " ) ;
ret = true ;
}
else
msg ( M_WARN , " NOTE: Release of DHCP-assigned IP address lease on TAP-Win32 adapter failed: %s (code=%u) " ,
strerror_win32 ( status , & gc ) ,
( unsigned int ) status ) ;
}
}
gc_free ( & gc ) ;
return ret ;
}
bool
dhcp_renew ( const struct tuntap * tt )
{
struct gc_arena gc = gc_new ( ) ;
bool ret = false ;
if ( tt & & tt - > options . ip_win32_type = = IPW32_SET_DHCP_MASQ & & tt - > adapter_index ! = ~ 0 )
{
const IP_ADAPTER_INDEX_MAP * inter = get_interface_info ( tt - > adapter_index , & gc ) ;
if ( inter )
{
DWORD status = IpRenewAddress ( ( IP_ADAPTER_INDEX_MAP * ) inter ) ;
if ( status = = NO_ERROR )
{
msg ( D_TUNTAP_INFO , " TAP: DHCP address renewal succeeded " ) ;
ret = true ;
}
else
msg ( M_WARN , " WARNING: Failed to renew DHCP IP address lease on TAP-Win32 adapter: %s (code=%u) " ,
strerror_win32 ( status , & gc ) ,
( unsigned int ) status ) ;
}
}
gc_free ( & gc ) ;
return ret ;
}
2005-10-31 04:01:17 +01:00
/*
* netsh functions
*/
static void
netsh_command ( const char * cmd , int n )
{
int i ;
for ( i = 0 ; i < n ; + + i )
{
bool status ;
2005-12-22 19:09:40 +01:00
openvpn_sleep ( 1 ) ;
2005-10-31 04:01:17 +01:00
netcmd_semaphore_lock ( ) ;
msg ( M_INFO , " NETSH: %s " , cmd ) ;
status = system_check ( cmd , NULL , 0 , " ERROR: netsh command failed " ) ;
netcmd_semaphore_release ( ) ;
if ( status )
return ;
2005-12-22 19:09:40 +01:00
openvpn_sleep ( 4 ) ;
2005-10-31 04:01:17 +01:00
}
msg ( M_FATAL , " NETSH: command failed " ) ;
}
2005-12-22 19:09:40 +01:00
void
ip_addr_string_to_array ( in_addr_t * dest , int * dest_len , const IP_ADDR_STRING * src )
{
int i = 0 ;
while ( src )
{
const unsigned int getaddr_flags = GETADDR_HOST_ORDER ;
const char * ip_str = src - > IpAddress . String ;
in_addr_t ip = 0 ;
bool succeed = false ;
if ( i > = * dest_len )
break ;
if ( ! ip_str | | ! strlen ( ip_str ) )
break ;
ip = getaddr ( getaddr_flags , ip_str , 0 , & succeed , NULL ) ;
if ( ! succeed )
break ;
dest [ i + + ] = ip ;
src = src - > Next ;
}
* dest_len = i ;
#if 0
{
struct gc_arena gc = gc_new ( ) ;
msg ( M_INFO , " ip_addr_string_to_array [%d] " , * dest_len ) ;
for ( i = 0 ; i < * dest_len ; + + i )
{
msg ( M_INFO , " %s " , print_in_addr_t ( dest [ i ] , 0 , & gc ) ) ;
}
gc_free ( & gc ) ;
}
# endif
}
static bool
ip_addr_one_to_one ( const in_addr_t * a1 , const int a1len , const IP_ADDR_STRING * ias )
{
in_addr_t a2 [ 8 ] ;
int a2len = SIZE ( a2 ) ;
int i ;
ip_addr_string_to_array ( a2 , & a2len , ias ) ;
/*msg (M_INFO, "a1len=%d a2len=%d", a1len, a2len);*/
if ( a1len ! = a2len )
return false ;
for ( i = 0 ; i < a1len ; + + i )
{
if ( a1 [ i ] ! = a2 [ i ] )
return false ;
}
return true ;
}
static bool
ip_addr_member_of ( const in_addr_t addr , const IP_ADDR_STRING * ias )
{
in_addr_t aa [ 8 ] ;
int len = SIZE ( aa ) ;
int i ;
ip_addr_string_to_array ( aa , & len , ias ) ;
for ( i = 0 ; i < len ; + + i )
{
if ( addr = = aa [ i ] )
return true ;
}
return false ;
}
static void
netsh_ifconfig_options ( const char * type ,
const in_addr_t * addr_list ,
const int addr_len ,
const IP_ADDR_STRING * current ,
const char * flex_name ,
const bool test_first )
{
struct gc_arena gc = gc_new ( ) ;
struct buffer out = alloc_buf_gc ( 256 , & gc ) ;
bool delete_first = false ;
/* first check if we should delete existing DNS/WINS settings from TAP interface */
if ( test_first )
{
if ( ! ip_addr_one_to_one ( addr_list , addr_len , current ) )
delete_first = true ;
}
else
delete_first = true ;
/* delete existing DNS/WINS settings from TAP interface */
if ( delete_first )
{
buf_init ( & out , 0 ) ;
buf_printf ( & out , " netsh interface ip delete %s \" %s \" all " ,
type ,
flex_name ) ;
netsh_command ( BSTR ( & out ) , 2 ) ;
}
/* add new DNS/WINS settings to TAP interface */
{
int count = 0 ;
int i ;
for ( i = 0 ; i < addr_len ; + + i )
{
if ( delete_first | | ! test_first | | ! ip_addr_member_of ( addr_list [ i ] , current ) )
{
const char * fmt = count ?
" netsh interface ip add %s \" %s \" %s "
: " netsh interface ip set %s \" %s \" static %s " ;
buf_init ( & out , 0 ) ;
buf_printf ( & out , fmt ,
type ,
flex_name ,
print_in_addr_t ( addr_list [ i ] , 0 , & gc ) ) ;
netsh_command ( BSTR ( & out ) , 2 ) ;
+ + count ;
}
else
{
msg ( M_INFO , " NETSH: \" %s \" %s %s [already set] " ,
flex_name ,
type ,
print_in_addr_t ( addr_list [ i ] , 0 , & gc ) ) ;
}
}
}
gc_free ( & gc ) ;
}
static void
init_ip_addr_string2 ( IP_ADDR_STRING * dest , const IP_ADDR_STRING * src1 , const IP_ADDR_STRING * src2 )
{
CLEAR ( dest [ 0 ] ) ;
CLEAR ( dest [ 1 ] ) ;
if ( src1 )
{
dest [ 0 ] = * src1 ;
dest [ 0 ] . Next = NULL ;
}
if ( src2 )
{
dest [ 1 ] = * src2 ;
dest [ 0 ] . Next = & dest [ 1 ] ;
dest [ 1 ] . Next = NULL ;
}
}
2005-10-31 04:01:17 +01:00
static void
netsh_ifconfig ( const struct tuntap_options * to ,
const char * flex_name ,
const in_addr_t ip ,
const in_addr_t netmask ,
2005-12-22 19:09:40 +01:00
const unsigned int flags )
2005-10-31 04:01:17 +01:00
{
struct gc_arena gc = gc_new ( ) ;
struct buffer out = alloc_buf_gc ( 256 , & gc ) ;
const IP_ADAPTER_INFO * ai = NULL ;
const IP_PER_ADAPTER_INFO * pai = NULL ;
if ( flags & NI_TEST_FIRST )
{
const IP_ADAPTER_INFO * list = get_adapter_info_list ( & gc ) ;
const int index = get_adapter_index_flexible ( flex_name ) ;
ai = get_adapter ( list , index ) ;
pai = get_per_adapter_info ( index , & gc ) ;
}
if ( flags & NI_IP_NETMASK )
{
if ( test_adapter_ip_netmask ( ai , ip , netmask ) )
{
msg ( M_INFO , " NETSH: \" %s \" %s/%s [already set] " ,
flex_name ,
print_in_addr_t ( ip , 0 , & gc ) ,
print_in_addr_t ( netmask , 0 , & gc ) ) ;
}
else
{
/* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
buf_init ( & out , 0 ) ;
buf_printf ( & out ,
" netsh interface ip set address \" %s \" static %s %s " ,
flex_name ,
print_in_addr_t ( ip , 0 , & gc ) ,
print_in_addr_t ( netmask , 0 , & gc ) ) ;
netsh_command ( BSTR ( & out ) , 4 ) ;
}
}
2005-12-22 19:09:40 +01:00
/* set WINS/DNS options */
if ( flags & NI_OPTIONS )
{
IP_ADDR_STRING wins [ 2 ] ;
CLEAR ( wins [ 0 ] ) ;
CLEAR ( wins [ 1 ] ) ;
netsh_ifconfig_options ( " dns " ,
to - > dns ,
to - > dns_len ,
pai ? & pai - > DnsServerList : NULL ,
flex_name ,
BOOL_CAST ( flags & NI_TEST_FIRST ) ) ;
if ( ai & & ai - > HaveWins )
init_ip_addr_string2 ( wins , & ai - > PrimaryWinsServer , & ai - > SecondaryWinsServer ) ;
netsh_ifconfig_options ( " wins " ,
to - > wins ,
to - > wins_len ,
ai ? wins : NULL ,
flex_name ,
BOOL_CAST ( flags & NI_TEST_FIRST ) ) ;
}
2005-10-31 04:01:17 +01:00
gc_free ( & gc ) ;
}
static void
netsh_enable_dhcp ( const struct tuntap_options * to ,
const char * actual_name )
{
struct gc_arena gc = gc_new ( ) ;
struct buffer out = alloc_buf_gc ( 256 , & gc ) ;
/* example: netsh interface ip set address my-tap dhcp */
buf_printf ( & out ,
" netsh interface ip set address \" %s \" dhcp " ,
actual_name ) ;
netsh_command ( BSTR ( & out ) , 4 ) ;
gc_free ( & gc ) ;
}
/*
* Return a TAP name for netsh commands .
*/
static const char *
netsh_get_id ( const char * dev_node , struct gc_arena * gc )
{
const struct tap_reg * tap_reg = get_tap_reg ( gc ) ;
const struct panel_reg * panel_reg = get_panel_reg ( gc ) ;
struct buffer actual = alloc_buf_gc ( 256 , gc ) ;
const char * guid ;
at_least_one_tap_win32 ( tap_reg ) ;
if ( dev_node )
{
guid = get_device_guid ( dev_node , BPTR ( & actual ) , BCAP ( & actual ) , tap_reg , panel_reg , gc ) ;
}
else
{
guid = get_unspecified_device_guid ( 0 , BPTR ( & actual ) , BCAP ( & actual ) , tap_reg , panel_reg , gc ) ;
if ( get_unspecified_device_guid ( 1 , NULL , 0 , tap_reg , panel_reg , gc ) ) /* ambiguous if more than one TAP-Win32 adapter */
guid = NULL ;
}
if ( ! guid )
return " NULL " ; /* not found */
else if ( strcmp ( BPTR ( & actual ) , " NULL " ) )
return BPTR ( & actual ) ; /* control panel name */
else
return guid ; /* no control panel name, return GUID instead */
}
/*
* Called iteratively on TAP - Win32 wait - for - initialization polling loop
*/
void
tun_standby_init ( struct tuntap * tt )
{
tt - > standby_iter = 0 ;
}
2005-12-22 19:09:40 +01:00
bool
2005-10-31 04:01:17 +01:00
tun_standby ( struct tuntap * tt )
{
2005-12-22 19:09:40 +01:00
bool ret = true ;
2005-10-31 04:01:17 +01:00
+ + tt - > standby_iter ;
2005-12-22 19:09:40 +01:00
if ( tt - > options . ip_win32_type = = IPW32_SET_ADAPTIVE )
2005-10-31 04:01:17 +01:00
{
2005-12-22 19:09:40 +01:00
if ( tt - > standby_iter = = IPW32_SET_ADAPTIVE_TRY_NETSH )
{
msg ( M_INFO , " NOTE: now trying netsh (this may take some time) " ) ;
netsh_ifconfig ( & tt - > options ,
tt - > actual_name ,
tt - > local ,
tt - > adapter_netmask ,
NI_TEST_FIRST | NI_IP_NETMASK | NI_OPTIONS ) ;
}
else if ( tt - > standby_iter > = IPW32_SET_ADAPTIVE_TRY_NETSH * 2 )
{
ret = false ;
}
2005-10-31 04:01:17 +01:00
}
2005-12-22 19:09:40 +01:00
return ret ;
2005-10-31 04:01:17 +01:00
}
2005-09-26 07:28:27 +02:00
/*
* Convert DHCP options from the command line / config file
* into a raw DHCP - format options string .
*/
static void
write_dhcp_u8 ( struct buffer * buf , const int type , const int data )
{
if ( ! buf_safe ( buf , 3 ) )
msg ( M_FATAL , " write_dhcp_u8: buffer overflow building DHCP options " ) ;
buf_write_u8 ( buf , type ) ;
buf_write_u8 ( buf , 1 ) ;
buf_write_u8 ( buf , data ) ;
}
static void
write_dhcp_u32_array ( struct buffer * buf , const int type , const uint32_t * data , const unsigned int len )
{
if ( len > 0 )
{
int i ;
const int size = len * sizeof ( uint32_t ) ;
if ( ! buf_safe ( buf , 2 + size ) )
msg ( M_FATAL , " write_dhcp_u32_array: buffer overflow building DHCP options " ) ;
if ( size < 1 | | size > 255 )
msg ( M_FATAL , " write_dhcp_u32_array: size (%d) must be > 0 and <= 255 " , size ) ;
buf_write_u8 ( buf , type ) ;
buf_write_u8 ( buf , size ) ;
for ( i = 0 ; i < len ; + + i )
buf_write_u32 ( buf , data [ i ] ) ;
}
}
static void
write_dhcp_str ( struct buffer * buf , const int type , const char * str )
{
const int len = strlen ( str ) ;
if ( ! buf_safe ( buf , 2 + len ) )
msg ( M_FATAL , " write_dhcp_str: buffer overflow building DHCP options " ) ;
if ( len < 1 | | len > 255 )
msg ( M_FATAL , " write_dhcp_str: string '%s' must be > 0 bytes and <= 255 bytes " , str ) ;
buf_write_u8 ( buf , type ) ;
buf_write_u8 ( buf , len ) ;
buf_write ( buf , str , len ) ;
}
static void
build_dhcp_options_string ( struct buffer * buf , const struct tuntap_options * o )
{
if ( o - > domain )
write_dhcp_str ( buf , 15 , o - > domain ) ;
if ( o - > netbios_scope )
write_dhcp_str ( buf , 47 , o - > netbios_scope ) ;
if ( o - > netbios_node_type )
write_dhcp_u8 ( buf , 46 , o - > netbios_node_type ) ;
write_dhcp_u32_array ( buf , 6 , ( uint32_t * ) o - > dns , o - > dns_len ) ;
write_dhcp_u32_array ( buf , 44 , ( uint32_t * ) o - > wins , o - > wins_len ) ;
write_dhcp_u32_array ( buf , 42 , ( uint32_t * ) o - > ntp , o - > ntp_len ) ;
write_dhcp_u32_array ( buf , 45 , ( uint32_t * ) o - > nbdd , o - > nbdd_len ) ;
/* the MS DHCP server option 'Disable Netbios-over-TCP/IP
is implemented as vendor option 001 , value 002.
A value of 001 means ' leave NBT alone ' which is the default */
if ( o - > disable_nbt )
{
buf_write_u8 ( buf , 43 ) ;
buf_write_u8 ( buf , 6 ) ; /* total length field */
buf_write_u8 ( buf , 0x001 ) ;
buf_write_u8 ( buf , 4 ) ; /* length of the vendor specified field */
buf_write_u32 ( buf , 0x002 ) ;
}
}
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
struct gc_arena gc = gc_new ( ) ;
char device_path [ 256 ] ;
const char * device_guid = NULL ;
DWORD len ;
2005-10-31 04:01:17 +01:00
bool dhcp_masq = false ;
bool dhcp_masq_post = false ;
2005-09-26 07:28:27 +02:00
/*netcmd_semaphore_lock ();*/
ipv6_support ( ipv6 , false , tt ) ;
if ( tt - > type = = DEV_TYPE_NULL )
{
open_null ( tt ) ;
gc_free ( & gc ) ;
return ;
}
else if ( tt - > type = = DEV_TYPE_TAP | | tt - > type = = DEV_TYPE_TUN )
{
;
}
else
{
msg ( M_FATAL | M_NOPREFIX , " Unknown virtual device type: '%s' " , dev ) ;
}
/*
* Lookup the device name in the registry , using the - - dev - node high level name .
*/
{
const struct tap_reg * tap_reg = get_tap_reg ( & gc ) ;
const struct panel_reg * panel_reg = get_panel_reg ( & gc ) ;
2005-10-31 04:01:17 +01:00
char actual_buffer [ 256 ] ;
2005-09-26 07:28:27 +02:00
at_least_one_tap_win32 ( tap_reg ) ;
if ( dev_node )
{
/* Get the device GUID for the device specified with --dev-node. */
2005-10-31 04:01:17 +01:00
device_guid = get_device_guid ( dev_node , actual_buffer , sizeof ( actual_buffer ) , tap_reg , panel_reg , & gc ) ;
2005-09-26 07:28:27 +02:00
if ( ! device_guid )
2005-09-26 09:40:02 +02:00
msg ( M_FATAL , " TAP-Win32 adapter '%s' not found " , dev_node ) ;
2005-09-26 07:28:27 +02:00
/* Open Windows TAP-Win32 adapter */
openvpn_snprintf ( device_path , sizeof ( device_path ) , " %s%s%s " ,
USERMODEDEVICEDIR ,
device_guid ,
TAPSUFFIX ) ;
tt - > hand = CreateFile (
device_path ,
GENERIC_READ | GENERIC_WRITE ,
0 , /* was: FILE_SHARE_READ */
0 ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED ,
0
) ;
if ( tt - > hand = = INVALID_HANDLE_VALUE )
msg ( M_ERR , " CreateFile failed on TAP device: %s " , device_path ) ;
}
else
{
int device_number = 0 ;
/* Try opening all TAP devices until we find one available */
while ( true )
{
device_guid = get_unspecified_device_guid ( device_number ,
2005-10-31 04:01:17 +01:00
actual_buffer ,
sizeof ( actual_buffer ) ,
2005-09-26 07:28:27 +02:00
tap_reg ,
panel_reg ,
& gc ) ;
if ( ! device_guid )
msg ( M_FATAL , " All TAP-Win32 adapters on this system are currently in use. " ) ;
/* Open Windows TAP-Win32 adapter */
openvpn_snprintf ( device_path , sizeof ( device_path ) , " %s%s%s " ,
USERMODEDEVICEDIR ,
device_guid ,
TAPSUFFIX ) ;
tt - > hand = CreateFile (
device_path ,
GENERIC_READ | GENERIC_WRITE ,
0 , /* was: FILE_SHARE_READ */
0 ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED ,
0
) ;
if ( tt - > hand = = INVALID_HANDLE_VALUE )
msg ( D_TUNTAP_INFO , " CreateFile failed on TAP device: %s " , device_path ) ;
else
break ;
device_number + + ;
}
}
/* translate high-level device name into a device instance
GUID using the registry */
2005-10-31 04:01:17 +01:00
tt - > actual_name = string_alloc ( actual_buffer , NULL ) ;
2005-09-26 07:28:27 +02:00
}
msg ( M_INFO , " TAP-WIN32 device [%s] opened: %s " , tt - > actual_name , device_path ) ;
2005-10-31 04:01:17 +01:00
tt - > adapter_index = get_adapter_index ( device_guid ) ;
2005-09-26 07:28:27 +02:00
/* get driver version info */
{
ULONG info [ 3 ] ;
CLEAR ( info ) ;
if ( DeviceIoControl ( tt - > hand , TAP_IOCTL_GET_VERSION ,
& info , sizeof ( info ) ,
& info , sizeof ( info ) , & len , NULL ) )
{
msg ( D_TUNTAP_INFO , " TAP-Win32 Driver Version %d.%d %s " ,
( int ) info [ 0 ] ,
( int ) info [ 1 ] ,
( info [ 2 ] ? " (DEBUG) " : " " ) ) ;
}
if ( ! ( info [ 0 ] > TAP_WIN32_MIN_MAJOR
| | ( info [ 0 ] = = TAP_WIN32_MIN_MAJOR & & info [ 1 ] > = TAP_WIN32_MIN_MINOR ) ) )
msg ( M_FATAL , " ERROR: This version of " PACKAGE_NAME " requires a TAP-Win32 driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver. " ,
TAP_WIN32_MIN_MAJOR ,
TAP_WIN32_MIN_MINOR ) ;
}
/* get driver MTU */
{
ULONG mtu ;
if ( DeviceIoControl ( tt - > hand , TAP_IOCTL_GET_MTU ,
& mtu , sizeof ( mtu ) ,
& mtu , sizeof ( mtu ) , & len , NULL ) )
{
tt - > post_open_mtu = ( int ) mtu ;
msg ( D_MTU_INFO , " TAP-Win32 MTU=%d " , ( int ) mtu ) ;
}
}
2005-10-31 04:01:17 +01:00
/*
* Preliminaries for setting TAP - Win32 adapter TCP / IP
* properties via - - ip - win32 dynamic or - - ip - win32 adaptive .
*/
if ( tt - > did_ifconfig_setup )
{
if ( tt - > options . ip_win32_type = = IPW32_SET_DHCP_MASQ )
{
/*
* If adapter is set to non - DHCP , set to DHCP mode .
*/
if ( dhcp_status ( tt - > adapter_index ) = = DHCP_STATUS_DISABLED )
netsh_enable_dhcp ( & tt - > options , tt - > actual_name ) ;
dhcp_masq = true ;
dhcp_masq_post = true ;
}
else if ( tt - > options . ip_win32_type = = IPW32_SET_ADAPTIVE )
{
/*
* If adapter is set to non - DHCP , use netsh right away .
*/
if ( dhcp_status ( tt - > adapter_index ) ! = DHCP_STATUS_ENABLED )
{
netsh_ifconfig ( & tt - > options ,
tt - > actual_name ,
tt - > local ,
tt - > adapter_netmask ,
NI_TEST_FIRST | NI_IP_NETMASK | NI_OPTIONS ) ;
}
else
{
dhcp_masq = true ;
}
}
}
2005-09-26 07:28:27 +02:00
/* set point-to-point mode if TUN device */
if ( tt - > type = = DEV_TYPE_TUN )
{
if ( ! tt - > did_ifconfig_setup )
{
msg ( M_FATAL , " ERROR: --dev tun also requires --ifconfig " ) ;
}
2005-09-26 09:40:02 +02:00
if ( tt - > topology = = TOP_SUBNET )
{
in_addr_t ep [ 3 ] ;
BOOL status ;
ep [ 0 ] = htonl ( tt - > local ) ;
ep [ 1 ] = htonl ( tt - > local & tt - > remote_netmask ) ;
ep [ 2 ] = htonl ( tt - > remote_netmask ) ;
status = DeviceIoControl ( tt - > hand , TAP_IOCTL_CONFIG_TUN ,
ep , sizeof ( ep ) ,
ep , sizeof ( ep ) , & len , NULL ) ;
msg ( status ? M_INFO : M_FATAL , " Set TAP-Win32 TUN subnet mode network/local/netmask = %s/%s/%s [%s] " ,
print_in_addr_t ( ep [ 1 ] , IA_NET_ORDER , & gc ) ,
print_in_addr_t ( ep [ 0 ] , IA_NET_ORDER , & gc ) ,
print_in_addr_t ( ep [ 2 ] , IA_NET_ORDER , & gc ) ,
status ? " SUCCEEDED " : " FAILED " ) ;
} else {
in_addr_t ep [ 2 ] ;
ep [ 0 ] = htonl ( tt - > local ) ;
ep [ 1 ] = htonl ( tt - > remote_netmask ) ;
if ( ! DeviceIoControl ( tt - > hand , TAP_IOCTL_CONFIG_POINT_TO_POINT ,
ep , sizeof ( ep ) ,
ep , sizeof ( ep ) , & len , NULL ) )
msg ( M_FATAL , " ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun " ) ;
}
2005-09-26 07:28:27 +02:00
}
/* should we tell the TAP-Win32 driver to masquerade as a DHCP server as a means
of setting the adapter address ? */
2005-10-31 04:01:17 +01:00
if ( dhcp_masq )
2005-09-26 07:28:27 +02:00
{
uint32_t ep [ 4 ] ;
/* We will answer DHCP requests with a reply to set IP/subnet to these values */
ep [ 0 ] = htonl ( tt - > local ) ;
ep [ 1 ] = htonl ( tt - > adapter_netmask ) ;
/* At what IP address should the DHCP server masquerade at? */
if ( tt - > type = = DEV_TYPE_TUN )
{
2005-09-26 09:40:02 +02:00
if ( tt - > topology = = TOP_SUBNET )
{
const in_addr_t netmask_inv = ~ tt - > remote_netmask ;
ep [ 2 ] = netmask_inv ? htonl ( ( tt - > local | netmask_inv ) - 1 ) : 0 ;
}
else
ep [ 2 ] = htonl ( tt - > remote_netmask ) ;
2005-09-26 07:28:27 +02:00
if ( tt - > options . dhcp_masq_custom_offset )
msg ( M_WARN , " WARNING: because you are using '--dev tun' mode, the '--ip-win32 dynamic [offset]' option is ignoring the offset parameter " ) ;
}
else
{
in_addr_t dsa ; /* DHCP server addr */
ASSERT ( tt - > type = = DEV_TYPE_TAP ) ;
if ( tt - > options . dhcp_masq_offset < 0 )
dsa = ( tt - > local | ( ~ tt - > adapter_netmask ) ) + tt - > options . dhcp_masq_offset ;
else
dsa = ( tt - > local & tt - > adapter_netmask ) + tt - > options . dhcp_masq_offset ;
if ( dsa = = tt - > local )
msg ( M_FATAL , " ERROR: There is a clash between the --ifconfig local address and the internal DHCP server address -- both are set to %s -- please use the --ip-win32 dynamic option to choose a different free address from the --ifconfig subnet for the internal DHCP server " , print_in_addr_t ( dsa , 0 , & gc ) ) ;
if ( ( tt - > local & tt - > adapter_netmask ) ! = ( dsa & tt - > adapter_netmask ) )
msg ( M_FATAL , " ERROR: --tap-win32 dynamic [offset] : offset is outside of --ifconfig subnet " ) ;
ep [ 2 ] = htonl ( dsa ) ;
}
/* lease time in seconds */
ep [ 3 ] = ( uint32_t ) tt - > options . dhcp_lease_time ;
ASSERT ( ep [ 3 ] > 0 ) ;
2005-12-22 19:09:40 +01:00
# ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad DHCP negotiation */
2005-09-26 07:28:27 +02:00
if ( ! DeviceIoControl ( tt - > hand , TAP_IOCTL_CONFIG_DHCP_MASQ ,
ep , sizeof ( ep ) ,
ep , sizeof ( ep ) , & len , NULL ) )
msg ( M_FATAL , " ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set TAP_IOCTL_CONFIG_DHCP_MASQ mode " ) ;
msg ( M_INFO , " Notified TAP-Win32 driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d] " ,
print_in_addr_t ( tt - > local , 0 , & gc ) ,
print_in_addr_t ( tt - > adapter_netmask , 0 , & gc ) ,
device_guid ,
print_in_addr_t ( ep [ 2 ] , IA_NET_ORDER , & gc ) ,
ep [ 3 ]
) ;
/* user-supplied DHCP options capability */
if ( tt - > options . dhcp_options )
{
struct buffer buf = alloc_buf ( 256 ) ;
build_dhcp_options_string ( & buf , & tt - > options ) ;
msg ( D_DHCP_OPT , " DHCP option string: %s " , format_hex ( BPTR ( & buf ) , BLEN ( & buf ) , 0 , & gc ) ) ;
if ( ! DeviceIoControl ( tt - > hand , TAP_IOCTL_CONFIG_DHCP_SET_OPT ,
BPTR ( & buf ) , BLEN ( & buf ) ,
BPTR ( & buf ) , BLEN ( & buf ) , & len , NULL ) )
msg ( M_FATAL , " ERROR: The TAP-Win32 driver rejected a TAP_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call " ) ;
free_buf ( & buf ) ;
}
2005-12-22 19:09:40 +01:00
# endif
2005-09-26 07:28:27 +02:00
}
/* set driver media status to 'connected' */
{
ULONG status = TRUE ;
if ( ! DeviceIoControl ( tt - > hand , TAP_IOCTL_SET_MEDIA_STATUS ,
& status , sizeof ( status ) ,
& status , sizeof ( status ) , & len , NULL ) )
msg ( M_WARN , " WARNING: The TAP-Win32 driver rejected a TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call. " ) ;
}
/* possible wait for adapter to come up */
{
int s = tt - > options . tap_sleep ;
if ( s > 0 )
{
msg ( M_INFO , " Sleeping for %d seconds... " , s ) ;
openvpn_sleep ( s ) ;
}
}
/* possibly use IP Helper API to set IP address on adapter */
{
2005-10-31 04:01:17 +01:00
const DWORD index = tt - > adapter_index ;
2005-09-26 07:28:27 +02:00
/* flush arp cache */
if ( index ! = ( DWORD ) ~ 0 )
{
DWORD status ;
if ( ( status = FlushIpNetTable ( index ) ) = = NO_ERROR )
msg ( M_INFO , " Successful ARP Flush on interface [%u] %s " ,
( unsigned int ) index ,
device_guid ) ;
else
2005-09-26 09:40:02 +02:00
msg ( D_TUNTAP_INFO , " NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s " ,
2005-09-26 07:28:27 +02:00
( unsigned int ) index ,
device_guid ,
( unsigned int ) status ,
strerror_win32 ( status , & gc ) ) ;
}
/*
* If the TAP - Win32 driver is masquerading as a DHCP server
* make sure the TCP / IP properties for the adapter are
* set correctly .
*/
2005-10-31 04:01:17 +01:00
if ( dhcp_masq_post )
2005-09-26 07:28:27 +02:00
{
/* check dhcp enable status */
2005-10-31 04:01:17 +01:00
if ( dhcp_status ( index ) = = DHCP_STATUS_DISABLED )
2005-09-26 07:28:27 +02:00
msg ( M_WARN , " WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically' " ) ;
/* force an explicit DHCP lease renewal on TAP adapter? */
if ( tt - > options . dhcp_pre_release )
dhcp_release ( tt ) ;
if ( tt - > options . dhcp_renew )
dhcp_renew ( tt ) ;
}
if ( tt - > did_ifconfig_setup & & tt - > options . ip_win32_type = = IPW32_SET_IPAPI )
{
DWORD status ;
const char * error_suffix = " I am having trouble using the Windows 'IP helper API' to automatically set the IP address -- consider using other --ip-win32 methods (not 'ipapi') " ;
/* couldn't get adapter index */
if ( index = = ( DWORD ) ~ 0 )
{
msg ( M_FATAL , " ERROR: unable to get adapter index for interface %s -- %s " ,
device_guid ,
error_suffix ) ;
}
/* check dhcp enable status */
2005-10-31 04:01:17 +01:00
if ( dhcp_status ( index ) = = DHCP_STATUS_DISABLED )
2005-09-26 07:28:27 +02:00
msg ( M_WARN , " NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically' " ) ;
/* delete previously added IP addresses which were not
correctly deleted */
delete_temp_addresses ( index ) ;
/* add a new IP address */
if ( ( status = AddIPAddress ( htonl ( tt - > local ) ,
htonl ( tt - > adapter_netmask ) ,
index ,
& tt - > ipapi_context ,
& tt - > ipapi_instance ) ) = = NO_ERROR )
msg ( M_INFO , " Succeeded in adding a temporary IP/netmask of %s/%s to interface %s using the Win32 IP Helper API " ,
print_in_addr_t ( tt - > local , 0 , & gc ) ,
print_in_addr_t ( tt - > adapter_netmask , 0 , & gc ) ,
device_guid
) ;
else
msg ( M_FATAL , " ERROR: AddIPAddress %s/%s failed on interface %s, index=%d, status=%u (windows error: '%s') -- %s " ,
print_in_addr_t ( tt - > local , 0 , & gc ) ,
print_in_addr_t ( tt - > adapter_netmask , 0 , & gc ) ,
device_guid ,
( int ) index ,
( unsigned int ) status ,
strerror_win32 ( status , & gc ) ,
error_suffix ) ;
tt - > ipapi_context_defined = true ;
}
}
/*netcmd_semaphore_release ();*/
gc_free ( & gc ) ;
}
const char *
tap_win32_getinfo ( const struct tuntap * tt , struct gc_arena * gc )
{
if ( tt & & tt - > hand ! = NULL )
{
struct buffer out = alloc_buf_gc ( 256 , gc ) ;
DWORD len ;
if ( DeviceIoControl ( tt - > hand , TAP_IOCTL_GET_INFO ,
BSTR ( & out ) , BCAP ( & out ) ,
BSTR ( & out ) , BCAP ( & out ) ,
& len , NULL ) )
{
return BSTR ( & out ) ;
}
}
return NULL ;
}
void
tun_show_debug ( struct tuntap * tt )
{
if ( tt & & tt - > hand ! = NULL )
{
struct buffer out = alloc_buf ( 1024 ) ;
DWORD len ;
while ( DeviceIoControl ( tt - > hand , TAP_IOCTL_GET_LOG_LINE ,
BSTR ( & out ) , BCAP ( & out ) ,
BSTR ( & out ) , BCAP ( & out ) ,
& len , NULL ) )
{
msg ( D_TAP_WIN32_DEBUG , " TAP-Win32: %s " , BSTR ( & out ) ) ;
}
free_buf ( & out ) ;
}
}
void
close_tun ( struct tuntap * tt )
{
struct gc_arena gc = gc_new ( ) ;
if ( tt )
{
# if 1
if ( tt - > ipapi_context_defined )
{
DWORD status ;
if ( ( status = DeleteIPAddress ( tt - > ipapi_context ) ) ! = NO_ERROR )
{
msg ( M_WARN , " Warning: DeleteIPAddress[%u] failed on TAP-Win32 adapter, status=%u : %s " ,
( unsigned int ) tt - > ipapi_context ,
( unsigned int ) status ,
strerror_win32 ( status , & gc ) ) ;
}
}
# endif
if ( tt - > options . dhcp_release )
dhcp_release ( tt ) ;
if ( tt - > hand ! = NULL )
{
dmsg ( D_WIN32_IO_LOW , " Attempting CancelIO on TAP-Win32 adapter " ) ;
if ( ! CancelIo ( tt - > hand ) )
msg ( M_WARN | M_ERRNO , " Warning: CancelIO failed on TAP-Win32 adapter " ) ;
}
dmsg ( D_WIN32_IO_LOW , " Attempting close of overlapped read event on TAP-Win32 adapter " ) ;
overlapped_io_close ( & tt - > reads ) ;
dmsg ( D_WIN32_IO_LOW , " Attempting close of overlapped write event on TAP-Win32 adapter " ) ;
overlapped_io_close ( & tt - > writes ) ;
if ( tt - > hand ! = NULL )
{
dmsg ( D_WIN32_IO_LOW , " Attempting CloseHandle on TAP-Win32 adapter " ) ;
if ( ! CloseHandle ( tt - > hand ) )
msg ( M_WARN | M_ERRNO , " Warning: CloseHandle failed on TAP-Win32 adapter " ) ;
}
if ( tt - > actual_name )
free ( tt - > actual_name ) ;
clear_tuntap ( tt ) ;
free ( tt ) ;
}
gc_free ( & gc ) ;
}
/*
* Convert - - ip - win32 constants between index and ascii form .
*/
struct ipset_names {
const char * short_form ;
} ;
/* Indexed by IPW32_SET_x */
static const struct ipset_names ipset_names [ ] = {
{ " manual " } ,
{ " netsh " } ,
{ " ipapi " } ,
2005-10-31 04:01:17 +01:00
{ " dynamic " } ,
{ " adaptive " }
2005-09-26 07:28:27 +02:00
} ;
int
ascii2ipset ( const char * name )
{
int i ;
ASSERT ( IPW32_SET_N = = SIZE ( ipset_names ) ) ;
for ( i = 0 ; i < IPW32_SET_N ; + + i )
if ( ! strcmp ( name , ipset_names [ i ] . short_form ) )
return i ;
return - 1 ;
}
const char *
ipset2ascii ( int index )
{
ASSERT ( IPW32_SET_N = = SIZE ( ipset_names ) ) ;
if ( index < 0 | | index > = IPW32_SET_N )
return " [unknown --ip-win32 type] " ;
else
return ipset_names [ index ] . short_form ;
}
const char *
ipset2ascii_all ( struct gc_arena * gc )
{
struct buffer out = alloc_buf_gc ( 256 , gc ) ;
int i ;
ASSERT ( IPW32_SET_N = = SIZE ( ipset_names ) ) ;
for ( i = 0 ; i < IPW32_SET_N ; + + i )
{
if ( i )
buf_printf ( & out , " " ) ;
buf_printf ( & out , " [%s] " , ipset2ascii ( i ) ) ;
}
return BSTR ( & out ) ;
}
# else /* generic */
void
open_tun ( const char * dev , const char * dev_type , const char * dev_node , bool ipv6 , struct tuntap * tt )
{
open_tun_generic ( dev , dev_type , dev_node , ipv6 , false , true , tt ) ;
}
void
close_tun ( struct tuntap * tt )
{
if ( tt )
{
close_tun_generic ( tt ) ;
free ( tt ) ;
}
}
int
write_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
return write ( tt - > fd , buf , len ) ;
}
int
read_tun ( struct tuntap * tt , uint8_t * buf , int len )
{
return read ( tt - > fd , buf , len ) ;
}
# endif