summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.ipv681
-rw-r--r--TODO.ipv630
-rw-r--r--acinclude.m46
-rw-r--r--buffer.c17
-rw-r--r--buffer.h5
-rw-r--r--configure.ac16
-rw-r--r--init.c43
-rwxr-xr-x[-rw-r--r--]install-win32/makeopenvpn62
-rw-r--r--manage.c10
-rw-r--r--mroute.c30
-rw-r--r--mtcp.c5
-rw-r--r--multi.c38
-rw-r--r--occ.c2
-rw-r--r--openvpn.810
-rw-r--r--options.c100
-rw-r--r--ps.c6
-rw-r--r--route.c24
-rw-r--r--socket.c1062
-rw-r--r--socket.h245
-rw-r--r--socks.c18
-rw-r--r--syshead.h12
-rw-r--r--tun.c2
-rw-r--r--win32.h5
23 files changed, 1564 insertions, 265 deletions
diff --git a/README.ipv6 b/README.ipv6
new file mode 100644
index 0000000..4295f85
--- /dev/null
+++ b/README.ipv6
@@ -0,0 +1,81 @@
+[ Last updated: 25-Mar-2011. ]
+
+OpenVPN-2.1 over UDP6/TCP6 README for ipv6-0.4.x patch releases:
+( --udp6 and --tcp6-{client,server} )
+
+* Availability
+ Source code under GPLv2 from http://github.com/jjo/openvpn-ipv6
+
+ Distro ready repos/packages:
+ o Debian sid official repo, by Alberto Gonzalez Iniesta,
+ starting from openvpn_2.1~rc20-2
+ o Gentoo official portage tree, by Marcel Pennewiss:
+ - https://bugs.gentoo.org/show_bug.cgi?id=287896
+ o Ubuntu package, by Bernhard Schmidt:
+ - https://launchpad.net/~berni/+archive/ipv6/+packages
+ o Freetz.org, milestone freetz-1.2
+ - http://trac.freetz.org/milestone/freetz-1.2
+
+* Status:
+ o OK:
+ - upd6,tcp6: GNU/Linux, win32, openbsd-4.7, freebsd-8.1
+ - udp4->upd6,tcp4->tcp6 (ipv4/6 mapped): GNU/Linux
+ (gives a warning on local!=remote proto matching)
+ o NOT:
+ - win32: tcp4->tcp6 (ipv4/6 mapped) fails w/connection refused
+ o NOT tested:
+ - mgmt console
+
+* Build setup:
+ ./configure --enable-ipv6 (by default)
+
+* Usage:
+ For IPv6 just specify "-p upd6" an proper IPv6 hostnames, adapting the example
+ from man page ...
+
+ On may:
+ openvpn --proto udp6 --remote <june_IPv6_addr> --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 --verb 5 --secret key
+
+ On june:
+ openvpn --proto udp6 --remote <may_IPv6_addr> --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 --verb 5 --secret key
+
+ Same for --proto tcp6-client, tcp6-server.
+
+* Main code changes summary:
+ - socket.h: New struct openvpn_sockaddr type that holds sockaddrs and pktinfo,
+ (here I omitted #ifdef USE_PF_xxxx, see socket.h )
+
+ struct openvpn_sockaddr {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } addr;
+ };
+
+ struct link_socket_addr
+ {
+ struct openvpn_sockaddr local;
+ struct openvpn_sockaddr remote;
+ struct openvpn_sockaddr actual;
+ };
+
+ PRO: allows simple type overloading: local.addr.sa, local.addr.in, local.addr.in6 ... etc
+ (also local.pi.in and local.pi.in6)
+
+ - several function prototypes moved from sockaddr_in to openvpn_sockaddr
+ - several new sockaddr functions needed to "generalize" AF_xxxx operations:
+ addr_copy(), addr_zero(), ...etc
+ proto_is_udp(), proto_is_dgram(), proto_is_net()
+
+* TODO: See TODO.ipv6
+
+--
+JuanJo Ciarlante jjo () google () com ............................
+: :
+. Linux IP Aliasing author .
+. Modular algo (AES et all) support for FreeSWAN/OpenSWAN author .
+. OpenVPN over IPv6 support .
+:...... plus other scattered free software bits in the wild ...:
diff --git a/TODO.ipv6 b/TODO.ipv6
new file mode 100644
index 0000000..966af2d
--- /dev/null
+++ b/TODO.ipv6
@@ -0,0 +1,30 @@
+[ Last updated: 11-Nov-2009. ]
+
+* All platforms:
+ o mgmt console: as currently passes straight in_addr_t bits around
+
+ o make possible to get AF from getaddrinfo() answer, ie allow openvpn to
+ use ipv4/6 if DNS returns A/AAAA without specifying protocol.
+ Hard: requires deep changes in initialization/calling logic
+
+ o use AI_PASSIVE
+
+ o the getaddr()/getaddr6() interface is not prepared for handling socktype
+ "tagging", currently I abuse the sockflags bits for getting the ai_socktype
+ downstream.
+
+ o implement comparison for mapped addesses: server in dual stack
+ listening IPv6 must permit incoming streams from allowed IPv4 peer,
+ currently you need to pass eg: --remote ffff::1.2.3.4
+
+ o do something with multi mode learn routes, for now just ignoring
+ ipv6 addresses seems the most sensible thing to do, because there's
+ no support for intra-tunnel ipv6 stuff.
+
+* win32:
+ o find out about mapped addresses, as I can't make it work
+ with bound at ::1 and connect to 127.0.0.1
+
+* N/A:
+ o this is ipv6 *endpoint* support, so don't expect "ifconfig6"-like
+ support in this patch
diff --git a/acinclude.m4 b/acinclude.m4
index 185907f..acfc01d 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -123,5 +123,9 @@ AC_DEFUN([TYPE_SOCKLEN_T],
AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv,
[type to use in place of socklen_t if not defined])],
[#include <sys/types.h>
-#include <sys/socket.h>])
+#ifdef WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif])
])
diff --git a/buffer.c b/buffer.c
index 3fa0cd3..51cf7c1 100644
--- a/buffer.c
+++ b/buffer.c
@@ -214,6 +214,23 @@ buf_printf (struct buffer *buf, const char *format, ...)
return ret;
}
+bool
+buf_puts(struct buffer *buf, const char *str)
+{
+ int ret = false;
+ uint8_t *ptr = BEND (buf);
+ int cap = buf_forward_capacity (buf);
+ if (cap > 0)
+ {
+ strncpynt ((char *)ptr,str, cap);
+ *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
+ buf->len += (int) strlen ((char *)ptr);
+ ret = true;
+ }
+ return ret;
+}
+
+
/*
* This is necessary due to certain buggy implementations of snprintf,
* that don't guarantee null termination for size > 0.
diff --git a/buffer.h b/buffer.h
index 3b24d09..0a394a7 100644
--- a/buffer.h
+++ b/buffer.h
@@ -277,6 +277,11 @@ bool buf_printf (struct buffer *buf, const char *format, ...)
;
/*
+ * puts append to a buffer with overflow check
+ */
+bool buf_puts (struct buffer *buf, const char *str);
+
+/*
* Like snprintf but guarantees null termination for size > 0
*/
int openvpn_snprintf(char *str, size_t size, const char *format, ...)
diff --git a/configure.ac b/configure.ac
index e0847bc..c3bd8e8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -146,6 +146,12 @@ AC_ARG_ENABLE(multihome,
[MULTIHOME="yes"]
)
+AC_ARG_ENABLE(ipv6,
+ [ --disable-ipv6 Disable UDP/IPv6 support],
+ [PF_INET6="$enableval"],
+ [PF_INET6="yes"]
+)
+
AC_ARG_ENABLE(port-share,
[ --disable-port-share Disable TCP server port-share support (--port-share)],
[PORT_SHARE="$enableval"],
@@ -566,6 +572,16 @@ LDFLAGS="$LDFLAGS -Wl,--fatal-warnings"
AC_CHECK_FUNC(epoll_create, AC_DEFINE(HAVE_EPOLL_CREATE, 1, [epoll_create function is defined]))
LDFLAGS="$OLDLDFLAGS"
+dnl ipv6 support
+if test "$PF_INET6" = "yes"; then
+ AC_CHECKING([for struct sockaddr_in6 for IPv6 support])
+ AC_CHECK_TYPE(
+ [struct sockaddr_in6],
+ [AC_DEFINE(USE_PF_INET6, 1, [struct sockaddr_in6 is needed for IPv6 peer support])],
+ [],
+ [#include "syshead.h"])
+fi
+
dnl
dnl check for valgrind tool
dnl
diff --git a/init.c b/init.c
index a51b7d4..ed508b8 100644
--- a/init.c
+++ b/init.c
@@ -96,7 +96,7 @@ update_options_ce_post (struct options *options)
*/
if (options->pull
&& options->ping_rec_timeout_action == PING_UNDEF
- && options->ce.proto == PROTO_UDPv4)
+ && proto_is_dgram(options->ce.proto))
{
options->ping_rec_timeout = PRE_PULL_INITIAL_PING_RESTART;
options->ping_rec_timeout_action = PING_RESTART;
@@ -1150,7 +1150,12 @@ initialization_sequence_completed (struct context *c, const unsigned int flags)
const char *detail = "SUCCESS";
if (c->c1.tuntap)
tun_local = c->c1.tuntap->local;
- tun_remote = htonl (c->c1.link_socket_addr.actual.dest.sa.sin_addr.s_addr);
+ /* TODO(jjo): for ipv6 this will convert some 32bits in the ipv6 addr
+ * to a meaningless ipv4 address.
+ * In any case, is somewhat inconsistent to send local tunnel
+ * addr with remote _endpoint_ addr (?)
+ */
+ tun_remote = htonl (c->c1.link_socket_addr.actual.dest.addr.in4.sin_addr.s_addr);
if (flags & ISC_ERRORS)
detail = "ERROR";
management_set_state (management,
@@ -1569,7 +1574,7 @@ do_deferred_options (struct context *c, const unsigned int found)
#ifdef ENABLE_OCC
if (found & OPT_P_EXPLICIT_NOTIFY)
{
- if (c->options.ce.proto != PROTO_UDPv4 && c->options.explicit_exit_notification)
+ if (!proto_is_udp(c->options.ce.proto) && c->options.explicit_exit_notification)
{
msg (D_PUSH, "OPTIONS IMPORT: --explicit-exit-notify can only be used with --proto udp");
c->options.explicit_exit_notification = 0;
@@ -1664,13 +1669,22 @@ socket_restart_pause (struct context *c)
switch (c->options.ce.proto)
{
case PROTO_UDPv4:
+#ifdef USE_PF_INET6
+ case PROTO_UDPv6:
+#endif
if (proxy)
sec = c->options.ce.connect_retry_seconds;
break;
case PROTO_TCPv4_SERVER:
+#ifdef USE_PF_INET6
+ case PROTO_TCPv6_SERVER:
+#endif
sec = 1;
break;
case PROTO_TCPv4_CLIENT:
+#ifdef USE_PF_INET6
+ case PROTO_TCPv6_CLIENT:
+#endif
sec = c->options.ce.connect_retry_seconds;
break;
}
@@ -2810,7 +2824,7 @@ do_setup_fast_io (struct context *c)
#ifdef WIN32
msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows");
#else
- if (c->options.ce.proto != PROTO_UDPv4)
+ if (!proto_is_udp(c->options.ce.proto))
msg (M_INFO, "NOTE: --fast-io is disabled since we are not using UDP");
else
{
@@ -3086,7 +3100,11 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int
/* link_socket_mode allows CM_CHILD_TCP
instances to inherit acceptable fds
from a top-level parent */
- if (c->options.ce.proto == PROTO_TCPv4_SERVER)
+ if (c->options.ce.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || c->options.ce.proto == PROTO_TCPv6_SERVER
+#endif
+ )
{
if (c->mode == CM_TOP)
link_socket_mode = LS_MODE_TCP_LISTEN;
@@ -3361,17 +3379,8 @@ inherit_context_child (struct context *dest,
{
CLEAR (*dest);
- switch (src->options.ce.proto)
- {
- case PROTO_UDPv4:
- dest->mode = CM_CHILD_UDP;
- break;
- case PROTO_TCPv4_SERVER:
- dest->mode = CM_CHILD_TCP;
- break;
- default:
- ASSERT (0);
- }
+ /* proto_is_dgram will ASSERT(0) if proto is invalid */
+ dest->mode = proto_is_dgram(src->options.ce.proto)? CM_CHILD_UDP : CM_CHILD_TCP;
dest->gc = gc_new ();
@@ -3477,7 +3486,7 @@ inherit_context_top (struct context *dest,
dest->c2.es_owned = false;
dest->c2.event_set = NULL;
- if (src->options.ce.proto == PROTO_UDPv4)
+ if (proto_is_dgram(src->options.ce.proto))
do_event_set_init (dest, false);
}
diff --git a/install-win32/makeopenvpn b/install-win32/makeopenvpn
index c1a805d..ced2a54 100644..100755
--- a/install-win32/makeopenvpn
+++ b/install-win32/makeopenvpn
@@ -2,35 +2,65 @@
H=`pwd`
-# get version.nsi definitions
-. autodefs/defs.sh
+case "`uname -o 2>/dev/null`" in
+ *inux)
-if gcc --version &>/dev/null && [ -d "$OPENSSL_DIR" ] && [ -d "$LZO_DIR" ] && [ -d "$PKCS11_HELPER_DIR" ]; then
+ # cross-compiling, make dude's life easier
+ XAUTOCONF="--host=i586-mingw32msvc --build=i386-linux $XAUTOCONF"
+ export CC=i586-mingw32msvc-gcc
+ export CXXCPP=i586-mingw32msvc-cpp
+ export CXX=i586-mingw32msvc-g++
+ # this requires the human to setup these environ vars:
+ # OPENSSL_DIR LZO_DIR PKCS11_HELPER_DIR
+ OPENSSL_INC_DIR=$OPENSSL_DIR/include
+ OPENSSL_LIB_DIR=$OPENSSL_DIR/out
+ LZO_INC_DIR=$LZO_DIR/include
+ LZO_LIB_DIR=$LZO_DIR/src/.libs
+ PKCS11_INC_DIR=$PKCS11_HELPER_DIR/include/pkcs11-helper-1.0/
+ PKCS11_LIB_DIR=$PKCS11_HELPER_DIR/lib/.libs/
+ ;;
+ *)
+ # get version.nsi definitions
+ . autodefs/defs.sh
+ XAUTOCONF=""
+ # default configuration creates relative-path environ vars:
+ OPENSSL_INC_DIR=$H/$OPENSSL_DIR/include
+ OPENSSL_LIB_DIR=$H/$OPENSSL_DIR/out
+ LZO_INC_DIR=$H/$LZO_DIR/include
+ LZO_LIB_DIR=$H/$LZO_DIR
+ PKCS11_INC_DIR=$H/$PKCS11_HELPER_DIR/usr/local/include
+ PKCS11_LIB_DIR=$H/$PKCS11_HELPER_DIR/usr/local/lib
+ ;;
+esac
+if $CC --version &>/dev/null && [ -d "$OPENSSL_DIR" ] && [ -d "$LZO_DIR" ] && [ -d "$PKCS11_HELPER_DIR" ]; then
# build OpenVPN binary
if ! [ -f Makefile ]; then
autoreconf -i -v \
- && ./configure \
+ && ./configure $XAUTOCONF \
--enable-strict \
--prefix=$H/windest \
MAN2HTML=true \
- --with-ssl-headers=$H/$OPENSSL_DIR/include \
- --with-ssl-lib=$H/$OPENSSL_DIR/out \
- --with-lzo-headers=$H/$LZO_DIR/include \
- --with-lzo-lib=$H/$LZO_DIR \
- --with-pkcs11-helper-headers=$H/$PKCS11_HELPER_DIR/usr/local/include \
- --with-pkcs11-helper-lib=$H/$PKCS11_HELPER_DIR/usr/local/lib
+ --with-ssl-headers=$OPENSSL_INC_DIR \
+ --with-ssl-lib=$OPENSSL_LIB_DIR \
+ --with-lzo-headers=$LZO_INC_DIR \
+ --with-lzo-lib=$LZO_LIB_DIR \
+ --with-pkcs11-helper-headers=$PKCS11_INC_DIR \
+ --with-pkcs11-helper-lib=$PKCS11_LIB_DIR \
+ || exit 1
fi
make -j $MAKE_JOBS && make install
+ if [ -n "$GENOOUT" ];then
# copy OpenVPN and service executables to GENOUT/bin
- mkdir -p $GENOUT/bin &>/dev/null
- cp windest/sbin/openvpn.exe $GENOUT/bin
- cp windest/sbin/openvpnserv.exe $GENOUT/bin
- if [ -z "$NO_STRIP" ]; then
- strip $GENOUT/bin/openvpn.exe
- strip $GENOUT/bin/openvpnserv.exe
+ mkdir -p $GENOUT/bin &>/dev/null
+ cp windest/sbin/openvpn.exe $GENOUT/bin
+ cp windest/sbin/openvpnserv.exe $GENOUT/bin
+ if [ -z "$NO_STRIP" ]; then
+ strip $GENOUT/bin/openvpn.exe
+ strip $GENOUT/bin/openvpnserv.exe
+ fi
fi
else
echo DID NOT BUILD openvpn.exe and openvpnserv.exe because one or more of gcc, OPENSSL_DIR, LZO_DIR, or PKCS11_HELPER_DIR directories were missing
diff --git a/manage.c b/manage.c
index 310a70e..a4fd258 100644
--- a/manage.c
+++ b/manage.c
@@ -1999,9 +1999,9 @@ man_settings_init (struct man_settings *ms,
/*
* Initialize socket address
*/
- ms->local.sa.sin_family = AF_INET;
- ms->local.sa.sin_addr.s_addr = 0;
- ms->local.sa.sin_port = htons (port);
+ ms->local.addr.in4.sin_family = AF_INET;
+ ms->local.addr.in4.sin_addr.s_addr = 0;
+ ms->local.addr.in4.sin_port = htons (port);
/*
* Run management over tunnel, or
@@ -2013,7 +2013,7 @@ man_settings_init (struct man_settings *ms,
}
else
{
- ms->local.sa.sin_addr.s_addr = getaddr
+ ms->local.addr.in4.sin_addr.s_addr = getaddr
(GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL);
}
}
@@ -2472,7 +2472,7 @@ management_post_tunnel_open (struct management *man, const in_addr_t tun_local_i
&& man->connection.state == MS_INITIAL)
{
/* listen on our local TUN/TAP IP address */
- man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip);
+ man->settings.local.addr.in4.sin_addr.s_addr = htonl (tun_local_ip);
man_connection_init (man);
}
diff --git a/mroute.c b/mroute.c
index 3debd80..a0f8e4e 100644
--- a/mroute.c
+++ b/mroute.c
@@ -226,25 +226,47 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
const struct openvpn_sockaddr *osaddr,
bool use_port)
{
- if (osaddr->sa.sin_family == AF_INET)
+ switch (osaddr->addr.sa.sa_family)
+ {
+ case AF_INET:
{
if (use_port)
{
addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
addr->netbits = 0;
addr->len = 6;
- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
- memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
+ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
+ memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2);
}
else
{
addr->type = MR_ADDR_IPV4;
addr->netbits = 0;
addr->len = 4;
- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
+ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
}
return true;
}
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ if (use_port)
+ {
+ addr->type = MR_ADDR_IPV6 | MR_WITH_PORT;
+ addr->netbits = 0;
+ addr->len = 18;
+ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
+ memcpy (addr->addr + 16, &osaddr->addr.in6.sin6_port, 2);
+ }
+ else
+ {
+ addr->type = MR_ADDR_IPV6;
+ addr->netbits = 0;
+ addr->len = 16;
+ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
+ }
+ return true;
+#endif
+ }
return false;
}
diff --git a/mtcp.c b/mtcp.c
index 314aa44..ade2cfb 100644
--- a/mtcp.c
+++ b/mtcp.c
@@ -150,6 +150,11 @@ multi_tcp_instance_specific_init (struct multi_context *m, struct multi_instance
ASSERT (mi->context.c2.link_socket);
ASSERT (mi->context.c2.link_socket->info.lsa);
ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM);
+ ASSERT (mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET
+#ifdef USE_PF_INET6
+ || mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET6
+#endif
+ );
if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true))
{
msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined");
diff --git a/multi.c b/multi.c
index cc3c4cb..df73cb9 100644
--- a/multi.c
+++ b/multi.c
@@ -1058,8 +1058,8 @@ multi_learn_in_addr_t (struct multi_context *m,
struct mroute_addr addr;
CLEAR (remote_si);
- remote_si.sa.sin_family = AF_INET;
- remote_si.sa.sin_addr.s_addr = htonl (a);
+ remote_si.addr.in4.sin_family = AF_INET;
+ remote_si.addr.in4.sin_addr.s_addr = htonl (a);
ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false));
if (netbits >= 0)
@@ -2496,9 +2496,9 @@ management_callback_kill_by_addr (void *arg, const in_addr_t addr, const int por
int count = 0;
CLEAR (saddr);
- saddr.sa.sin_family = AF_INET;
- saddr.sa.sin_addr.s_addr = htonl (addr);
- saddr.sa.sin_port = htons (port);
+ saddr.addr.in4.sin_family = AF_INET;
+ saddr.addr.in4.sin_addr.s_addr = htonl (addr);
+ saddr.addr.in4.sin_port = htons (port);
if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true))
{
hash_iterator_init (m->iter, &hi);
@@ -2675,16 +2675,24 @@ tunnel_server (struct context *top)
{
ASSERT (top->options.mode == MODE_SERVER);
- switch (top->options.ce.proto) {
- case PROTO_UDPv4:
- tunnel_server_udp (top);
- break;
- case PROTO_TCPv4_SERVER:
- tunnel_server_tcp (top);
- break;
- default:
- ASSERT (0);
- }
+#ifdef USE_PF_INET6
+ if (proto_is_dgram(top->options.ce.proto))
+ tunnel_server_udp(top);
+ else
+ tunnel_server_tcp(top);
+#else
+ switch (top->options.ce.proto)
+ {
+ case PROTO_UDPv4:
+ tunnel_server_udp (top);
+ break;
+ case PROTO_TCPv4_SERVER:
+ tunnel_server_tcp (top);
+ break;
+ default:
+ ASSERT (0);
+ }
+#endif
}
#else
diff --git a/occ.c b/occ.c
index b84b18d..bcf91cc 100644
--- a/occ.c
+++ b/occ.c
@@ -369,7 +369,7 @@ process_received_occ_msg (struct context *c)
c->c2.max_send_size_remote,
c->c2.max_recv_size_local);
if (!c->options.fragment
- && c->options.ce.proto == PROTO_UDPv4
+ && (proto_is_dgram(c->options.ce.proto))
&& c->c2.max_send_size_local > TUN_MTU_MIN
&& (c->c2.max_recv_size_remote < c->c2.max_send_size_local
|| c->c2.max_recv_size_local < c->c2.max_send_size_remote))
diff --git a/openvpn.8 b/openvpn.8
index 7d213f9..ff4938d 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -5450,13 +5450,16 @@ or
script execution.
.\"*********************************************************
.TP
-.B trusted_ip
+.B trusted_ip (or trusted_ip6)
Actual IP address of connecting client or peer which has been authenticated.
Set prior to execution of
.B \-\-ipchange, \-\-client-connect,
and
.B \-\-client-disconnect
scripts.
+If using ipv6 endpoints (udp6, tcp6),
+.B trusted_ip6
+will be set instead.
.\"*********************************************************
.TP
.B trusted_port
@@ -5468,7 +5471,7 @@ and
scripts.
.\"*********************************************************
.TP
-.B untrusted_ip
+.B untrusted_ip (or untrusted_ip6)
Actual IP address of connecting client or peer which has not been authenticated
yet. Sometimes used to
.B nmap
@@ -5480,6 +5483,9 @@ Set prior to execution of
and
.B \-\-auth-user-pass-verify
scripts.
+If using ipv6 endpoints (udp6, tcp6),
+.B untrusted_ip6
+will be set instead.
.\"*********************************************************
.TP
.B untrusted_port
diff --git a/options.c b/options.c
index d20578f..4e87a97 100644
--- a/options.c
+++ b/options.c
@@ -80,6 +80,12 @@ const char title_string[] =
#ifdef ENABLE_EUREPHIA
" [eurephia]"
#endif
+#if ENABLE_IP_PKTINFO
+ " [MH]"
+#endif
+#ifdef USE_PF_INET6
+ " [PF_INET6]"
+#endif
" built on " __DATE__
;
@@ -102,6 +108,9 @@ static const char usage_message[] =
"--proto p : Use protocol p for communicating with peer.\n"
" p = udp (default), tcp-server, or tcp-client\n"
"--proto-force p : only consider protocol p in list of connection profiles.\n"
+#ifdef USE_PF_INET6
+ " p = udp6, tcp6-server, or tcp6-client (ipv6)\n"
+#endif
"--connect-retry n : For --proto tcp-client, number of seconds to wait\n"
" between connection retries (default=%d).\n"
"--connect-timeout n : For --proto tcp-client, connection timeout (in seconds).\n"
@@ -1707,11 +1716,27 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
* Sanity check on TCP mode options
*/
- if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT)
- msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client");
+ if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ && ce->proto != PROTO_TCPv6_CLIENT
+#endif
+ )
+ msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client"
+#ifdef USE_PF_INET6
+ " or tcp6-client"
+#endif
+ );
- if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT)
- msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client");
+ if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ && ce->proto != PROTO_TCPv6_CLIENT
+#endif
+ )
+ msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client"
+#ifdef USE_PF_INET6
+ " or tcp6-client"
+#endif
+ );
/*
* Sanity check on MTU parameters
@@ -1720,7 +1745,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT);
#ifdef ENABLE_OCC
- if (ce->proto != PROTO_UDPv4 && options->mtu_test)
+ if (!proto_is_udp(ce->proto) && options->mtu_test)
msg (M_USAGE, "--mtu-test only makes sense with --proto udp");
#endif
@@ -1733,7 +1758,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
* Sanity check on --local, --remote, and --ifconfig
*/
- if (string_defined_equal (ce->local, ce->remote)
+ if (proto_is_net(ce->proto)
+ && string_defined_equal (ce->local, ce->remote)
&& ce->local_port == ce->remote_port)
msg (M_USAGE, "--remote and --local addresses are the same");
@@ -1798,16 +1824,20 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
*/
#ifdef ENABLE_FRAGMENT
- if (ce->proto != PROTO_UDPv4 && options->fragment)
+ if (!proto_is_udp(ce->proto) && options->fragment)
msg (M_USAGE, "--fragment can only be used with --proto udp");
#endif
#ifdef ENABLE_OCC
- if (ce->proto != PROTO_UDPv4 && options->explicit_exit_notification)
+ if (!proto_is_udp(ce->proto) && options->explicit_exit_notification)
msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp");
#endif
- if (!ce->remote && ce->proto == PROTO_TCPv4_CLIENT)
+ if (!ce->remote && (ce->proto == PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_CLIENT
+#endif
+ ))
msg (M_USAGE, "--remote MUST be used in TCP Client mode");
#ifdef ENABLE_HTTP_PROXY
@@ -1825,7 +1855,12 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode");
#endif
- if (ce->proto == PROTO_TCPv4_SERVER && connection_list_defined (options))
+ if ((ce->proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_SERVER
+#endif
+ )
+ && connection_list_defined (options))
msg (M_USAGE, "TCP server mode allows at most one --remote address");
#if P2MP_SERVER
@@ -1839,11 +1874,28 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "--mode server only works with --dev tun or --dev tap");
if (options->pull)
msg (M_USAGE, "--pull cannot be used with --mode server");
- if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+ if (!(proto_is_udp(ce->proto) || ce->proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_SERVER
+#endif
+ ))
+ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"
+#ifdef USE_PF_INET6
+ " or proto tcp6-server"
+#endif
+ );
#if PORT_SHARE
- if ((options->port_share_host || options->port_share_port) && ce->proto != PROTO_TCPv4_SERVER)
- msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server)");
+ if ((options->port_share_host || options->port_share_port) &&
+ (ce->proto != PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ && ce->proto != PROTO_TCPv6_SERVER
+#endif
+ ))
+ msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server"
+#ifdef USE_PF_INET6
+ " or tcp6-server"
+#endif
+ ")");
#endif
if (!options->tls_server)
msg (M_USAGE, "--mode server requires --tls-server");
@@ -1871,9 +1923,17 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "--inetd cannot be used with --mode server");
if (options->ipchange)
msg (M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)");
- if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
- if (ce->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per))
+ if (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_SERVER
+#endif
+ ))
+ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"
+#ifdef USE_PF_INET6
+ " or --proto tcp6-server"
+#endif
+ );
+ if (!proto_is_udp(ce->proto) && (options->cf_max || options->cf_per))
msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead.");
if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask)
msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode");
@@ -1964,7 +2024,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
/*
* Check consistency of replay options
*/
- if ((ce->proto != PROTO_UDPv4)
+ if ((!proto_is_udp(ce->proto))
&& (options->replay_window != defaults.replay_window
|| options->replay_time != defaults.replay_time))
msg (M_USAGE, "--replay-window only makes sense with --proto udp");
@@ -2137,6 +2197,10 @@ options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce)
{
if (ce->proto == PROTO_TCPv4)
ce->proto = PROTO_TCPv4_CLIENT;
+#ifdef USE_PF_INET6
+ else if (ce->proto == PROTO_TCPv6)
+ ce->proto = PROTO_TCPv6_CLIENT;
+#endif
}
#endif
diff --git a/ps.c b/ps.c
index ef48e36..e07078f 100644
--- a/ps.c
+++ b/ps.c
@@ -320,9 +320,9 @@ sock_addr_set (struct openvpn_sockaddr *osaddr,
const int port)
{
CLEAR (*osaddr);
- osaddr->sa.sin_family = AF_INET;
- osaddr->sa.sin_addr.s_addr = htonl (addr);
- osaddr->sa.sin_port = htons (port);
+ osaddr->addr.in4.sin_family = AF_INET;
+ osaddr->addr.in4.sin_addr.s_addr = htonl (addr);
+ osaddr->addr.in4.sin_port = htons (port);
}
static inline void
diff --git a/route.c b/route.c
index b5092fe..76a4b10 100644
--- a/route.c
+++ b/route.c
@@ -581,13 +581,23 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
if (!local)
{
/* route remote host to original default gateway */
- add_route3 (rl->spec.remote_host,
- ~0,
- rl->spec.net_gateway,
- tt,
- flags,
- es);
- rl->did_local = true;
+#ifdef USE_PF_INET6
+ /* if remote_host is not ipv4 (ie: ipv6), just skip
+ * adding this special /32 route */
+ if (rl->spec.remote_host != IPV4_INVALID_ADDR) {
+#endif
+ add_route3 (rl->spec.remote_host,
+ ~0,
+ rl->spec.net_gateway,
+ tt,
+ flags,
+ es);
+ rl->did_local = true;
+#ifdef USE_PF_INET6
+ } else {
+ dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled");
+ }
+#endif
}
/* route DHCP/DNS server traffic through original default gateway */
diff --git a/socket.c b/socket.c
index a49940d..1059d3a 100644
--- a/socket.c
+++ b/socket.c
@@ -36,10 +36,16 @@
#include "memdbg.h"
const int proto_overhead[] = { /* indexed by PROTO_x */
- IPv4_UDP_HEADER_SIZE,
+ 0,
+ IPv4_UDP_HEADER_SIZE, /* IPv4 */
IPv4_TCP_HEADER_SIZE,
IPv4_TCP_HEADER_SIZE,
- IPv4_TCP_HEADER_SIZE
+#ifdef USE_PF_INET6
+ IPv6_UDP_HEADER_SIZE, /* IPv6 */
+ IPv6_TCP_HEADER_SIZE,
+ IPv6_TCP_HEADER_SIZE,
+ IPv6_TCP_HEADER_SIZE,
+#endif
};
/*
@@ -276,6 +282,201 @@ getaddr_multi (unsigned int flags,
return (flags & GETADDR_HOST_ORDER) ? ntohl (ia.s_addr) : ia.s_addr;
}
+#ifdef USE_PF_INET6
+/*
+ * Translate IPv6 addr or hostname into struct addrinfo
+ * If resolve error, try again for
+ * resolve_retry_seconds seconds.
+ */
+bool
+getaddr6 (unsigned int flags,
+ const char *hostname,
+ int resolve_retry_seconds,
+ volatile int *signal_received,
+ int *gai_err,
+ struct sockaddr_in6 *in6)
+{
+ bool success;
+ struct addrinfo hints, *ai;
+ int status;
+ int sigrec = 0;
+ int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
+ struct gc_arena gc = gc_new ();
+
+ ASSERT(in6);
+
+ if (!hostname)
+ hostname = "::";
+
+ if (flags & GETADDR_RANDOMIZE)
+ hostname = hostname_randomize(hostname, &gc);
+
+ if (flags & GETADDR_MSG_VIRT_OUT)
+ msglevel |= M_MSG_VIRT_OUT;
+
+ CLEAR (ai);
+ success = false;
+
+ if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
+ && !signal_received)
+ signal_received = &sigrec;
+
+ /* try numeric ipv6 addr first */
+ CLEAR(hints);
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((status = getaddrinfo(hostname, NULL, &hints, &ai))==0)
+ {
+ *in6 = *((struct sockaddr_in6 *)(ai->ai_addr));
+ freeaddrinfo(ai);
+ ai = NULL;
+ }
+ if (gai_err)
+ *gai_err = status;
+
+
+ if (status != 0) /* parse as IPv6 address failed? */
+ {
+ const int fail_wait_interval = 5; /* seconds */
+ int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : (resolve_retry_seconds / fail_wait_interval);
+ const char *fmt;
+ int level = 0;
+ int err;
+
+ ai = NULL;
+
+ fmt = "RESOLVE: Cannot resolve host address: %s: %s";
+ if ((flags & GETADDR_MENTION_RESOLVE_RETRY)
+ && !resolve_retry_seconds)
+ fmt = "RESOLVE: Cannot resolve host address: %s: %s (I would have retried this name query if you had specified the --resolv-retry option.)";
+
+ if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL)
+ {
+ msg (msglevel, "RESOLVE: Cannot parse IPv6 address: %s", hostname);
+ goto done;
+ }
+
+#ifdef ENABLE_MANAGEMENT
+ if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
+ {
+ if (management)
+ management_set_state (management,
+ OPENVPN_STATE_RESOLVE,
+ NULL,
+ (in_addr_t)0,
+ (in_addr_t)0);
+ }
+#endif
+
+ /*
+ * Resolve hostname
+ */
+ while (true)
+ {
+ /* try hostname lookup */
+ hints.ai_flags = 0;
+ hints.ai_socktype = dnsflags_to_socktype(flags);
+ dmsg (D_SOCKET_DEBUG, "GETADDR6 flags=0x%04x ai_family=%d ai_socktype=%d",
+ flags, hints.ai_family, hints.ai_socktype);
+ err = getaddrinfo(hostname, NULL, &hints, &ai);
+
+ if (gai_err)
+ *gai_err = err;
+
+ if (signal_received)
+ {
+ get_signal (signal_received);
+ if (*signal_received) /* were we interrupted by a signal? */
+ {
+ if (0 == err) {
+ ASSERT(ai);
+ freeaddrinfo(ai);
+ ai = NULL;
+ }
+ if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
+ {
+ msg (level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
+ *signal_received = 0;
+ }
+ else
+ goto done;
+ }
+ }
+
+ /* success? */
+ if (0 == err)
+ break;
+
+ /* resolve lookup failed, should we
+ continue or fail? */
+
+ level = msglevel;
+ if (resolve_retries > 0)
+ level = D_RESOLVE_ERRORS;
+
+ msg (level,
+ fmt,
+ hostname,
+ gai_strerror(err));
+
+ if (--resolve_retries <= 0)
+ goto done;
+
+ openvpn_sleep (fail_wait_interval);
+ }
+
+ ASSERT(ai);
+
+ if (!ai->ai_next)
+ *in6 = *((struct sockaddr_in6*)(ai->ai_addr));
+ else
+ /* more than one address returned */
+ {
+ struct addrinfo *ai_cursor;
+ int n = 0;
+ /* count address list */
+ for (ai_cursor = ai; ai_cursor; ai_cursor = ai_cursor->ai_next) n++;
+ ASSERT (n >= 2);
+
+ msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d ipv6 addresses, choosing one by random",
+ hostname,
+ n);
+
+ /* choose address randomly, for basic load-balancing capability */
+ n--;
+ n %= get_random();
+ for (ai_cursor = ai; n; ai_cursor = ai_cursor->ai_next) n--;
+ *in6 = *((struct sockaddr_in6*)(ai_cursor->ai_addr));
+ }
+
+ freeaddrinfo(ai);
+ ai = NULL;
+
+ /* hostname resolve succeeded */
+ success = true;
+ }
+ else
+ {
+ /* IP address parse succeeded */
+ success = true;
+ }
+
+ done:
+ if (signal_received && *signal_received)
+ {
+ int level = 0;
+ if (flags & GETADDR_FATAL_ON_SIGNAL)
+ level = M_FATAL;
+ else if (flags & GETADDR_WARN_ON_SIGNAL)
+ level = M_WARN;
+ msg (level, "RESOLVE: signal received during DNS resolution attempt");
+ }
+
+ gc_free (&gc);
+ return success;
+}
+#endif /* USE_PF_INET6 */
+
/*
* We do our own inet_aton because the glibc function
* isn't very good about error checking.
@@ -410,20 +611,53 @@ update_remote (const char* host,
bool *changed,
const unsigned int sockflags)
{
- if (host && addr)
+ switch(addr->addr.sa.sa_family)
{
- const in_addr_t new_addr = getaddr (
- sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
- host,
- 1,
- NULL,
- NULL);
- if (new_addr && addr->sa.sin_addr.s_addr != new_addr)
+ case AF_INET:
+ if (host && addr)
{
- addr->sa.sin_addr.s_addr = new_addr;
- *changed = true;
+ const in_addr_t new_addr = getaddr (
+ sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
+ host,
+ 1,
+ NULL,
+ NULL);
+ if (new_addr && addr->addr.in4.sin_addr.s_addr != new_addr)
+ {
+ addr->addr.in4.sin_addr.s_addr = new_addr;
+ *changed = true;
+ }
}
- }
+ break;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ if (host && addr)
+ {
+ struct sockaddr_in6 sin6;
+ CLEAR(sin6);
+ int success = getaddr6 (
+ sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
+ host,
+ 1,
+ NULL,
+ NULL,
+ &sin6);
+ if ( success )
+ {
+ if (!IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr, &addr->addr.in6.sin6_addr))
+ {
+ int port = addr->addr.in6.sin6_port;
+ /* ipv6 requires also eg. sin6_scope_id => easier to fully copy and override port */
+ addr->addr.in6 = sin6;
+ addr->addr.in6.sin6_port = port;
+ }
+ }
+ }
+ break;
+#endif
+ default:
+ ASSERT(0);
+ }
}
static int
@@ -610,12 +844,62 @@ create_socket_udp (const unsigned int flags)
else if (flags & SF_USE_IP_PKTINFO)
{
int pad = 1;
- setsockopt (sd, SOL_IP, IP_PKTINFO, (void*)&pad, sizeof(pad));
+#ifdef IP_PKTINFO
+ if (setsockopt (sd, SOL_IP, IP_PKTINFO,
+ (void*)&pad, sizeof(pad)) < 0)
+ msg(M_SOCKERR, "UDP: failed setsockopt for IP_PKTINFO");
+#elif defined(IP_RECVDSTADDR)
+ if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR,
+ (void*)&pad, sizeof(pad)) < 0)
+ msg(M_SOCKERR, "UDP: failed setsockopt for IP_RECVDSTADDR");
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
}
#endif
return sd;
}
+#ifdef USE_PF_INET6
+static socket_descriptor_t
+create_socket_udp6 (const unsigned int flags)
+{
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ msg (M_SOCKERR, "UDP: Cannot create UDP6 socket");
+#if ENABLE_IP_PKTINFO
+ else if (flags & SF_USE_IP_PKTINFO)
+ {
+ int pad = 1;
+ if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ (void*)&pad, sizeof(pad)) < 0)
+ msg(M_SOCKERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");
+ }
+#endif
+ return sd;
+}
+
+static socket_descriptor_t
+create_socket_tcp6 (void)
+{
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ msg (M_SOCKERR, "Cannot create TCP6 socket");
+
+ /* set SO_REUSEADDR on socket */
+ {
+ int on = 1;
+ if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR,
+ (void *) &on, sizeof (on)) < 0)
+ msg (M_SOCKERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP6 socket");
+ }
+
+ return sd;
+}
+
+#endif
static void
create_socket (struct link_socket *sock)
{
@@ -623,6 +907,7 @@ create_socket (struct link_socket *sock)
if (sock->info.proto == PROTO_UDPv4)
{
sock->sd = create_socket_udp (sock->sockflags);
+ sock->sockflags |= SF_GETADDRINFO_DGRAM;
#ifdef ENABLE_SOCKS
if (sock->socks_proxy)
@@ -634,6 +919,18 @@ create_socket (struct link_socket *sock)
{
sock->sd = create_socket_tcp ();
}
+#ifdef USE_PF_INET6
+ else if (sock->info.proto == PROTO_TCPv6_SERVER
+ || sock->info.proto == PROTO_TCPv6_CLIENT)
+ {
+ sock->sd = create_socket_tcp6 ();
+ }
+ else if (sock->info.proto == PROTO_UDPv6)
+ {
+ sock->sd = create_socket_udp6 (sock->sockflags);
+ sock->sockflags |= SF_GETADDRINFO_DGRAM;
+ }
+#endif
else
{
ASSERT (0);
@@ -671,7 +968,12 @@ socket_do_accept (socket_descriptor_t sd,
struct link_socket_actual *act,
const bool nowait)
{
- socklen_t remote_len = sizeof (act->dest.sa);
+ /* af_addr_size WILL return 0 in this case if AFs other than AF_INET
+ * are compiled because act is empty here.
+ * could use getsockname() to support later remote_len check
+ */
+ socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family);
+ socklen_t remote_len = sizeof(act->dest.addr);
socket_descriptor_t new_sd = SOCKET_UNDEFINED;
CLEAR (*act);
@@ -679,7 +981,7 @@ socket_do_accept (socket_descriptor_t sd,
#ifdef HAVE_GETPEERNAME
if (nowait)
{
- new_sd = getpeername (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+ new_sd = getpeername (sd, &act->dest.addr.sa, &remote_len);
if (!socket_defined (new_sd))
msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: getpeername() failed");
@@ -692,7 +994,7 @@ socket_do_accept (socket_descriptor_t sd,
#endif
else
{
- new_sd = accept (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+ new_sd = accept (sd, &act->dest.addr.sa, &remote_len);
}
#if 0 /* For debugging only, test the effect of accept() failures */
@@ -708,7 +1010,8 @@ socket_do_accept (socket_descriptor_t sd,
{
msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: accept(%d) failed", sd);
}
- else if (remote_len != sizeof (act->dest.sa))
+ /* only valid if we have remote_len_af!=0 */
+ else if (remote_len_af && remote_len != remote_len_af)
{
msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len);
openvpn_close_socket (new_sd);
@@ -809,7 +1112,7 @@ socket_bind (socket_descriptor_t sd,
{
struct gc_arena gc = gc_new ();
- if (bind (sd, (struct sockaddr *) &local->sa, sizeof (local->sa)))
+ if (bind (sd, &local->addr.sa, af_addr_size(local->addr.sa.sa_family)))
{
const int errnum = openvpn_errno_socket ();
msg (M_FATAL, "%s: Socket bind failed on local address %s: %s",
@@ -830,7 +1133,7 @@ openvpn_connect (socket_descriptor_t sd,
#ifdef CONNECT_NONBLOCK
set_nonblock (sd);
- status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa));
+ status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family));
if (status)
status = openvpn_errno_socket ();
if (status == EINPROGRESS)
@@ -888,7 +1191,7 @@ openvpn_connect (socket_descriptor_t sd,
}
}
#else
- status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa));
+ status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family));
if (status)
status = openvpn_errno_socket ();
#endif
@@ -966,7 +1269,20 @@ socket_connect (socket_descriptor_t *sd,
if (*signal_received)
goto done;
- *sd = create_socket_tcp ();
+#ifdef USE_PF_INET6
+ switch(local->addr.sa.sa_family)
+ {
+ case PF_INET6:
+ *sd = create_socket_tcp6 ();
+ break;
+ case PF_INET:
+#endif
+ *sd = create_socket_tcp ();
+#ifdef USE_PF_INET6
+ break;
+ }
+#endif
+
if (bind_local)
socket_bind (*sd, local, "TCP Client");
update_remote (remote_dynamic, remote, remote_changed, sockflags);
@@ -1031,15 +1347,54 @@ resolve_bind_local (struct link_socket *sock)
/* resolve local address if undefined */
if (!addr_defined (&sock->info.lsa->local))
{
- sock->info.lsa->local.sa.sin_family = AF_INET;
- sock->info.lsa->local.sa.sin_addr.s_addr =
- (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
+#ifdef USE_PF_INET6
+ /* may return AF_{INET|INET6} guessed from local_host */
+ switch(addr_guess_family(sock->info.proto, sock->local_host))
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->local.addr.in4.sin_family = AF_INET;
+ sock->info.lsa->local.addr.in4.sin_addr.s_addr =
+ (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
+ sock->local_host,
+ 0,
+ NULL,
+ NULL)
+ : htonl (INADDR_ANY));
+ sock->info.lsa->local.addr.in4.sin_port = htons (sock->local_port);
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ {
+ int success;
+ int err;
+ CLEAR(sock->info.lsa->local.addr.in6);
+ if (sock->local_host)
+ {
+ success = getaddr6(GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
sock->local_host,
0,
NULL,
- NULL)
- : htonl (INADDR_ANY));
- sock->info.lsa->local.sa.sin_port = htons (sock->local_port);
+ &err,
+ &sock->info.lsa->local.addr.in6);
+ }
+ else
+ {
+ sock->info.lsa->local.addr.in6.sin6_family = AF_INET6;
+ sock->info.lsa->local.addr.in6.sin6_addr = in6addr_any;
+ success = true;
+ }
+ if (!success)
+ {
+ msg (M_FATAL, "getaddr6() failed for local \"%s\": %s",
+ sock->local_host,
+ gai_strerror(err));
+ }
+ sock->info.lsa->local.addr.in6.sin6_port = htons (sock->local_port);
+ }
+ break;
+ }
+#endif /* USE_PF_INET6 */
}
/* bind to local address/port */
@@ -1062,14 +1417,32 @@ resolve_remote (struct link_socket *sock,
volatile int *signal_received)
{
struct gc_arena gc = gc_new ();
+#ifdef USE_PF_INET6
+ int af;
+#endif
if (!sock->did_resolve_remote)
{
/* resolve remote address if undefined */
if (!addr_defined (&sock->info.lsa->remote))
{
- sock->info.lsa->remote.sa.sin_family = AF_INET;
- sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+#ifdef USE_PF_INET6
+ af = addr_guess_family(sock->info.proto, sock->remote_host);
+ switch(af)
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->remote.addr.in4.sin_family = AF_INET;
+ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = 0;
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ CLEAR(sock->info.lsa->remote.addr.in6);
+ sock->info.lsa->remote.addr.in6.sin6_family = AF_INET6;
+ sock->info.lsa->remote.addr.in6.sin6_addr = in6addr_any;
+ break;
+ }
+#endif
if (sock->remote_host)
{
@@ -1112,13 +1485,31 @@ resolve_remote (struct link_socket *sock,
ASSERT (0);
}
- sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr (
- flags,
- sock->remote_host,
- retry,
- &status,
- signal_received);
-
+#ifdef USE_PF_INET6
+ switch(af)
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = getaddr (
+ flags,
+ sock->remote_host,
+ retry,
+ &status,
+ signal_received);
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ status = getaddr6 (
+ flags,
+ sock->remote_host,
+ retry,
+ signal_received,
+ NULL,
+ &sock->info.lsa->remote.addr.in6);
+ break;
+ }
+#endif
+
dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
flags,
phase,
@@ -1138,8 +1529,19 @@ resolve_remote (struct link_socket *sock,
goto done;
}
}
-
- sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port);
+#ifdef USE_PF_INET6
+ switch(af)
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->remote.addr.in4.sin_port = htons (sock->remote_port);
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ sock->info.lsa->remote.addr.in6.sin6_port = htons (sock->remote_port);
+ break;
+ }
+#endif
}
/* should we re-use previous active remote address? */
@@ -1256,7 +1658,11 @@ link_socket_init_phase1 (struct link_socket *sock,
if (mode == LS_MODE_TCP_ACCEPT_FROM)
{
ASSERT (accept_from);
- ASSERT (sock->info.proto == PROTO_TCPv4_SERVER);
+ ASSERT (sock->info.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || sock->info.proto == PROTO_TCPv6_SERVER
+#endif
+ );
ASSERT (!sock->inetd);
sock->sd = accept_from->sd;
}
@@ -1313,7 +1719,11 @@ link_socket_init_phase1 (struct link_socket *sock,
/* were we started by inetd or xinetd? */
if (sock->inetd)
{
- ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT);
+ ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ && sock->info.proto != PROTO_TCPv6_CLIENT
+#endif
+ );
ASSERT (socket_defined (inetd_socket_descriptor));
sock->sd = inetd_socket_descriptor;
}
@@ -1362,7 +1772,34 @@ link_socket_init_phase2 (struct link_socket *sock,
/* were we started by inetd or xinetd? */
if (sock->inetd)
{
- if (sock->info.proto == PROTO_TCPv4_SERVER)
+ if (sock->info.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || sock->info.proto == PROTO_TCPv6_SERVER
+#endif
+ ) {
+ /* AF_INET as default (and fallback) for inetd */
+ sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET;
+#ifdef USE_PF_INET6
+#ifdef HAVE_GETSOCKNAME
+ {
+ /* inetd: hint family type for dest = local's */
+ struct openvpn_sockaddr local_addr;
+ socklen_t addrlen = sizeof(local_addr);
+ if (getsockname (sock->sd, (struct sockaddr *)&local_addr, &addrlen) == 0) {
+ sock->info.lsa->actual.dest.addr.sa.sa_family = local_addr.addr.sa.sa_family;
+ dmsg (D_SOCKET_DEBUG, "inetd(%s): using sa_family=%d from getsockname(%d)",
+ proto2ascii(sock->info.proto, false), local_addr.addr.sa.sa_family,
+ sock->sd);
+ } else
+ msg (M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET",
+ proto2ascii(sock->info.proto, false), sock->sd);
+ }
+#else
+ msg (M_WARN, "inetd(%s): this OS does not provide the getsockname() "
+ "function, using AF_INET",
+ proto2ascii(sock->info.proto, false));
+#endif
+#endif
sock->sd =
socket_listen_accept (sock->sd,
&sock->info.lsa->actual,
@@ -1372,6 +1809,7 @@ link_socket_init_phase2 (struct link_socket *sock,
false,
sock->inetd == INETD_NOWAIT,
signal_received);
+ }
ASSERT (!remote_changed);
if (*signal_received)
goto done;
@@ -1384,7 +1822,11 @@ link_socket_init_phase2 (struct link_socket *sock,
goto done;
/* TCP client/server */
- if (sock->info.proto == PROTO_TCPv4_SERVER)
+ if (sock->info.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ ||sock->info.proto == PROTO_TCPv6_SERVER
+#endif
+ )
{
switch (sock->mode)
{
@@ -1419,7 +1861,11 @@ link_socket_init_phase2 (struct link_socket *sock,
ASSERT (0);
}
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT)
+ else if (sock->info.proto == PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ ||sock->info.proto == PROTO_TCPv6_CLIENT
+#endif
+ )
{
#ifdef GENERAL_PROXY_SUPPORT
@@ -1506,8 +1952,8 @@ link_socket_init_phase2 (struct link_socket *sock,
sock->remote_port = sock->proxy_dest_port;
sock->did_resolve_remote = false;
- sock->info.lsa->actual.dest.sa.sin_addr.s_addr = 0;
- sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+ addr_zero_host(&sock->info.lsa->actual.dest);
+ addr_zero_host(&sock->info.lsa->remote);
resolve_remote (sock, 1, NULL, signal_received);
@@ -1522,7 +1968,7 @@ link_socket_init_phase2 (struct link_socket *sock,
if (remote_changed)
{
msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment");
- sock->info.lsa->remote.sa.sin_addr.s_addr = sock->info.lsa->actual.dest.sa.sin_addr.s_addr;
+ addr_copy_host(&sock->info.lsa->remote, &sock->info.lsa->actual.dest);
}
}
@@ -1708,13 +2154,20 @@ link_socket_bad_incoming_addr (struct buffer *buf,
{
struct gc_arena gc = gc_new ();
- msg (D_LINK_ERRORS,
- "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
- print_link_socket_actual (from_addr, &gc),
- (int)from_addr->dest.sa.sin_family,
- print_sockaddr (&info->lsa->remote, &gc));
+ switch(from_addr->dest.addr.sa.sa_family)
+ {
+ case AF_INET:
+#ifdef USE_PF_INET6
+ case AF_INET6:
+#endif
+ msg (D_LINK_ERRORS,
+ "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
+ print_link_socket_actual (from_addr, &gc),
+ (int)from_addr->dest.addr.sa.sa_family,
+ print_sockaddr (&info->lsa->remote, &gc));
+ break;
+ }
buf->len = 0;
-
gc_free (&gc);
}
@@ -1729,10 +2182,25 @@ link_socket_current_remote (const struct link_socket_info *info)
{
const struct link_socket_addr *lsa = info->lsa;
+/*
+ * This logic supports "redirect-gateway" semantic, which
+ * makes sense only for PF_INET routes over PF_INET endpoints
+ *
+ * Maybe in the future consider PF_INET6 endpoints also ...
+ * by now just ignore it
+ *
+ */
+#ifdef USE_PF_INET6
+ if (lsa->actual.dest.addr.sa.sa_family != AF_INET)
+ return IPV4_INVALID_ADDR;
+#else
+ ASSERT (lsa->actual.dest.addr.sa.sa_family == AF_INET);
+#endif
+
if (link_socket_actual_defined (&lsa->actual))
- return ntohl (lsa->actual.dest.sa.sin_addr.s_addr);
+ return ntohl (lsa->actual.dest.addr.in4.sin_addr.s_addr);
else if (addr_defined (&lsa->remote))
- return ntohl (lsa->remote.sa.sin_addr.s_addr);
+ return ntohl (lsa->remote.addr.in4.sin_addr.s_addr);
else
return 0;
}
@@ -1959,26 +2427,61 @@ print_sockaddr_ex (const struct openvpn_sockaddr *addr,
const unsigned int flags,
struct gc_arena *gc)
{
- if (addr)
+ struct buffer out = alloc_buf_gc (128, gc);
+ bool addr_is_defined;
+ addr_is_defined = addr_defined (addr);
+ if (!addr_is_defined) {
+ return "[undef]";
+ }
+#ifdef USE_PF_INET6
+ switch(addr->addr.sa.sa_family)
{
- struct buffer out = alloc_buf_gc (64, gc);
- const int port = ntohs (addr->sa.sin_port);
+ case AF_INET:
+#endif
+ {
+ const int port= ntohs (addr->addr.in4.sin_port);
+ buf_puts (&out, "[AF_INET]");
- if (!(flags & PS_DONT_SHOW_ADDR))
- buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
+ if (!(flags & PS_DONT_SHOW_ADDR))
+ buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->addr.in4.sin_addr) : "[undef]"));
- if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
- && port)
+ if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
+ && port)
+ {
+ if (separator)
+ buf_printf (&out, "%s", separator);
+
+ buf_printf (&out, "%d", port);
+ }
+ }
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
{
- if (separator)
- buf_printf (&out, "%s", separator);
+ const int port= ntohs (addr->addr.in6.sin6_port);
+ char buf[INET6_ADDRSTRLEN] = "";
+ buf_puts (&out, "[AF_INET6]");
+ if (addr_is_defined)
+ {
+ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6),
+ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST);
+ buf_puts (&out, buf);
+ }
+ if (((flags & PS_SHOW_PORT) || (addr_is_defined && (flags & PS_SHOW_PORT_IF_DEFINED)))
+ && port)
+ {
+ if (separator)
+ buf_puts (&out, separator);
- buf_printf (&out, "%d", port);
+ buf_printf (&out, "%d", port);
+ }
}
- return BSTR (&out);
+ break;
+ default:
+ ASSERT(0);
}
- else
- return "[NULL]";
+#endif
+ return BSTR (&out);
}
const char *
@@ -1987,6 +2490,10 @@ print_link_socket_actual (const struct link_socket_actual *act, struct gc_arena
return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc);
}
+#ifndef IF_NAMESIZE
+#define IF_NAMESIZE 16
+#endif
+
const char *
print_link_socket_actual_ex (const struct link_socket_actual *act,
const char *separator,
@@ -1995,15 +2502,54 @@ print_link_socket_actual_ex (const struct link_socket_actual *act,
{
if (act)
{
+ char ifname[IF_NAMESIZE] = "[undef]";
struct buffer out = alloc_buf_gc (128, gc);
buf_printf (&out, "%s", print_sockaddr_ex (&act->dest, separator, flags, gc));
#if ENABLE_IP_PKTINFO
- if ((flags & PS_SHOW_PKTINFO) && act->pi.ipi_spec_dst.s_addr)
+ if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act))
{
- struct openvpn_sockaddr sa;
- CLEAR (sa);
- sa.sa.sin_addr = act->pi.ipi_spec_dst;
- buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc));
+#ifdef USE_PF_INET6
+ switch(act->dest.addr.sa.sa_family)
+ {
+ case AF_INET:
+#endif
+ {
+ struct openvpn_sockaddr sa;
+ CLEAR (sa);
+ sa.addr.in4.sin_family = AF_INET;
+#ifdef IP_PKTINFO
+ sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
+ if_indextoname(act->pi.in4.ipi_ifindex, ifname);
+#elif defined(IP_RECVDSTADDR)
+ sa.addr.in4.sin_addr = act->pi.in4;
+ ifname[0]=0;
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
+ buf_printf (&out, " (via %s%%%s)",
+ print_sockaddr_ex (&sa, separator, 0, gc),
+ ifname);
+ }
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 sin6;
+ char buf[INET6_ADDRSTRLEN] = "[undef]";
+ CLEAR(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = act->pi.in6.ipi6_addr;
+ if_indextoname(act->pi.in6.ipi6_ifindex, ifname);
+ if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6),
+ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0)
+ buf_printf (&out, " (via %s%%%s)", buf, ifname);
+ else
+ buf_printf (&out, " (via [getnameinfo() err]%%%s)", ifname);
+ }
+ break;
+ }
+#endif /* USE_PF_INET6 */
+
}
#endif
return BSTR (&out);
@@ -2038,18 +2584,40 @@ setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openv
{
char name_buf[256];
- if (flags & SA_IP_PORT)
- openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix);
- else
- openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
+#ifdef USE_PF_INET6
+ char buf[128];
+ switch(addr->addr.sa.sa_family)
+ {
+ case AF_INET:
+#endif
+ if (flags & SA_IP_PORT)
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix);
+ else
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
- setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr));
+ setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr));
- if ((flags & SA_IP_PORT) && addr->sa.sin_port)
- {
- openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
- setenv_int (es, name_buf, ntohs (addr->sa.sin_port));
+ if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port)
+ {
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
+ setenv_int (es, name_buf, ntohs (addr->addr.in4.sin_port));
+ }
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip6", name_prefix);
+ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6),
+ buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
+ setenv_str (es, name_buf, buf);
+
+ if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port)
+ {
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
+ setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port));
+ }
+ break;
}
+#endif
}
void
@@ -2059,7 +2627,8 @@ setenv_in_addr_t (struct env_set *es, const char *name_prefix, in_addr_t addr, c
{
struct openvpn_sockaddr si;
CLEAR (si);
- si.sa.sin_addr.s_addr = htonl (addr);
+ si.addr.in4.sin_family = AF_INET;
+ si.addr.in4.sin_addr.s_addr = htonl (addr);
setenv_sockaddr (es, name_prefix, &si, flags);
}
}
@@ -2080,16 +2649,63 @@ setenv_link_socket_actual (struct env_set *es,
struct proto_names {
const char *short_form;
const char *display_form;
+ bool is_dgram;
+ bool is_net;
+ unsigned short proto_af;
};
/* Indexed by PROTO_x */
-static const struct proto_names proto_names[] = {
- {"udp", "UDPv4"},
- {"tcp-server", "TCPv4_SERVER"},
- {"tcp-client", "TCPv4_CLIENT"},
- {"tcp", "TCPv4"}
+static const struct proto_names proto_names[PROTO_N] = {
+ {"proto-uninitialized", "proto-NONE",0,0, AF_UNSPEC},
+ {"udp", "UDPv4",1,1, AF_INET},
+ {"tcp-server", "TCPv4_SERVER",0,1, AF_INET},
+ {"tcp-client", "TCPv4_CLIENT",0,1, AF_INET},
+ {"tcp", "TCPv4",0,1, AF_INET},
+#ifdef USE_PF_INET6
+ {"udp6" ,"UDPv6",1,1, AF_INET6},
+ {"tcp6-server","TCPv6_SERVER",0,1, AF_INET6},
+ {"tcp6-client","TCPv6_CLIENT",0,1, AF_INET6},
+ {"tcp6" ,"TCPv6",0,1, AF_INET6},
+#endif
};
+bool
+proto_is_net(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].is_net;
+}
+bool
+proto_is_dgram(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].is_dgram;
+}
+bool
+proto_is_udp(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].is_dgram&&proto_names[proto].is_net;
+}
+bool
+proto_is_tcp(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return (!proto_names[proto].is_dgram)&&proto_names[proto].is_net;
+}
+
+unsigned short
+proto_sa_family(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].proto_af;
+}
+
int
ascii2proto (const char* proto_name)
{
@@ -2129,6 +2745,45 @@ proto2ascii_all (struct gc_arena *gc)
return BSTR (&out);
}
+int
+addr_guess_family(int proto, const char *name)
+{
+#ifdef USE_PF_INET6
+ unsigned short ret;
+#endif
+ if (proto)
+ {
+ return proto_sa_family(proto); /* already stamped */
+ }
+#ifdef USE_PF_INET6
+ else
+ {
+ struct addrinfo hints , *ai;
+ int err;
+ CLEAR(hints);
+ hints.ai_flags = AI_NUMERICHOST;
+ err = getaddrinfo(name, NULL, &hints, &ai);
+ if ( 0 == err )
+ {
+ ret=ai->ai_family;
+ freeaddrinfo(ai);
+ return ret;
+ }
+ }
+#endif
+ return AF_INET; /* default */
+}
+const char *
+addr_family_name (int af)
+{
+ switch (af)
+ {
+ case AF_INET: return "AF_INET";
+ case AF_INET6: return "AF_INET6";
+ }
+ return "AF_UNSPEC";
+}
+
/*
* Given a local proto, return local proto
* if !remote, or compatible remote proto
@@ -2143,10 +2798,15 @@ proto_remote (int proto, bool remote)
ASSERT (proto >= 0 && proto < PROTO_N);
if (remote)
{
- if (proto == PROTO_TCPv4_SERVER)
- return PROTO_TCPv4_CLIENT;
- if (proto == PROTO_TCPv4_CLIENT)
- return PROTO_TCPv4_SERVER;
+ switch (proto)
+ {
+ case PROTO_TCPv4_SERVER: return PROTO_TCPv4_CLIENT;
+ case PROTO_TCPv4_CLIENT: return PROTO_TCPv4_SERVER;
+#ifdef USE_PF_INET6
+ case PROTO_TCPv6_SERVER: return PROTO_TCPv6_CLIENT;
+ case PROTO_TCPv6_CLIENT: return PROTO_TCPv6_SERVER;
+#endif
+ }
}
return proto;
}
@@ -2205,10 +2865,29 @@ link_socket_read_tcp (struct link_socket *sock,
#if ENABLE_IP_PKTINFO
#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */
-struct openvpn_pktinfo
+struct openvpn_in4_pktinfo
{
struct cmsghdr cmsghdr;
- struct in_pktinfo in_pktinfo;
+#ifdef HAVE_IN_PKTINFO
+ struct in_pktinfo pi4;
+#endif
+#ifdef IP_RECVDSTADDR
+ struct in_addr pi4;
+#endif
+};
+#ifdef USE_PF_INET6
+struct openvpn_in6_pktinfo
+{
+ struct cmsghdr cmsghdr;
+ struct in6_pktinfo pi6;
+};
+#endif
+
+union openvpn_pktinfo {
+ struct openvpn_in4_pktinfo msgpi4;
+#ifdef USE_PF_INET6
+ struct openvpn_in6_pktinfo msgpi6;
+#endif
};
#pragma pack()
@@ -2219,18 +2898,18 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
struct link_socket_actual *from)
{
struct iovec iov;
- struct openvpn_pktinfo opi;
+ union openvpn_pktinfo opi;
struct msghdr mesg;
- socklen_t fromlen = sizeof (from->dest.sa);
+ socklen_t fromlen = sizeof (from->dest.addr);
iov.iov_base = BPTR (buf);
iov.iov_len = maxsize;
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
- mesg.msg_name = &from->dest.sa;
+ mesg.msg_name = &from->dest.addr;
mesg.msg_namelen = fromlen;
mesg.msg_control = &opi;
- mesg.msg_controllen = sizeof (opi);
+ mesg.msg_controllen = sizeof opi;
buf->len = recvmsg (sock->sd, &mesg, 0);
if (buf->len >= 0)
{
@@ -2239,14 +2918,39 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
cmsg = CMSG_FIRSTHDR (&mesg);
if (cmsg != NULL
&& CMSG_NXTHDR (&mesg, cmsg) == NULL
+#ifdef IP_PKTINFO
&& cmsg->cmsg_level == SOL_IP
&& cmsg->cmsg_type == IP_PKTINFO
- && cmsg->cmsg_len >= sizeof (opi))
+#elif defined(IP_RECVDSTADDR)
+ && cmsg->cmsg_level == IPPROTO_IP
+ && cmsg->cmsg_type == IP_RECVDSTADDR
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
+ && cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo))
{
+#ifdef IP_PKTINFO
struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
- from->pi.ipi_ifindex = pkti->ipi_ifindex;
- from->pi.ipi_spec_dst = pkti->ipi_spec_dst;
+ from->pi.in4.ipi_ifindex = pkti->ipi_ifindex;
+ from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst;
+#elif defined(IP_RECVDSTADDR)
+ from->pi.in4 = *(struct in_addr*) CMSG_DATA (cmsg);
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
}
+#ifdef USE_PF_INET6
+ else if (cmsg != NULL
+ && CMSG_NXTHDR (&mesg, cmsg) == NULL
+ && cmsg->cmsg_level == IPPROTO_IPV6
+ && cmsg->cmsg_type == IPV6_PKTINFO
+ && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo))
+ {
+ struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
+ from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex;
+ from->pi.in6.ipi6_addr = pkti6->ipi6_addr;
+ }
+#endif
}
return fromlen;
}
@@ -2258,18 +2962,20 @@ link_socket_read_udp_posix (struct link_socket *sock,
int maxsize,
struct link_socket_actual *from)
{
- socklen_t fromlen = sizeof (from->dest.sa);
- from->dest.sa.sin_addr.s_addr = 0;
+ socklen_t fromlen = sizeof (from->dest.addr);
+ socklen_t expectedlen = af_addr_size(proto_sa_family(sock->info.proto));
+ addr_zero_host(&from->dest);
ASSERT (buf_safe (buf, maxsize));
#if ENABLE_IP_PKTINFO
- if (sock->sockflags & SF_USE_IP_PKTINFO)
+ /* Both PROTO_UDPv4 and PROTO_UDPv6 */
+ if (proto_is_udp(sock->info.proto) && sock->sockflags & SF_USE_IP_PKTINFO)
fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from);
else
#endif
buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0,
- (struct sockaddr *) &from->dest.sa, &fromlen);
- if (fromlen != sizeof (from->dest.sa))
- bad_address_length (fromlen, sizeof (from->dest.sa));
+ &from->dest.addr.sa, &fromlen);
+ if (buf->len >= 0 && expectedlen && fromlen != expectedlen)
+ bad_address_length (fromlen, expectedlen);
return buf->len;
}
@@ -2306,26 +3012,64 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
struct iovec iov;
struct msghdr mesg;
struct cmsghdr *cmsg;
- struct in_pktinfo *pkti;
- struct openvpn_pktinfo opi;
iov.iov_base = BPTR (buf);
iov.iov_len = BLEN (buf);
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
- mesg.msg_name = &to->dest.sa;
- mesg.msg_namelen = sizeof (to->dest.sa);
- mesg.msg_control = &opi;
- mesg.msg_controllen = sizeof (opi);
- mesg.msg_flags = 0;
- cmsg = CMSG_FIRSTHDR (&mesg);
- cmsg->cmsg_len = sizeof (opi);
- cmsg->cmsg_level = SOL_IP;
- cmsg->cmsg_type = IP_PKTINFO;
- pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
- pkti->ipi_ifindex = to->pi.ipi_ifindex;
- pkti->ipi_spec_dst = to->pi.ipi_spec_dst;
- pkti->ipi_addr.s_addr = 0;
+ switch (sock->info.lsa->remote.addr.sa.sa_family)
+ {
+ case AF_INET:
+ {
+ struct openvpn_in4_pktinfo msgpi4;
+ mesg.msg_name = &to->dest.addr.sa;
+ mesg.msg_namelen = sizeof (struct sockaddr_in);
+ mesg.msg_control = &msgpi4;
+ mesg.msg_controllen = sizeof msgpi4;
+ mesg.msg_flags = 0;
+ cmsg = CMSG_FIRSTHDR (&mesg);
+ cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo);
+#ifdef HAVE_IN_PKTINFO
+ cmsg->cmsg_level = SOL_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ {
+ struct in_pktinfo *pkti;
+ pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+ pkti->ipi_ifindex = to->pi.in4.ipi_ifindex;
+ pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst;
+ pkti->ipi_addr.s_addr = 0;
+ }
+#elif defined(IP_RECVDSTADDR)
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_RECVDSTADDR;
+ *(struct in_addr *) CMSG_DATA (cmsg) = to->pi.in4;
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
+ break;
+ }
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ {
+ struct openvpn_in6_pktinfo msgpi6;
+ struct in6_pktinfo *pkti6;
+ mesg.msg_name = &to->dest.addr.sa;
+ mesg.msg_namelen = sizeof (struct sockaddr_in6);
+ mesg.msg_control = &msgpi6;
+ mesg.msg_controllen = sizeof msgpi6;
+ mesg.msg_flags = 0;
+ cmsg = CMSG_FIRSTHDR (&mesg);
+ cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo);
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
+ pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex;
+ pkti6->ipi6_addr = to->pi.in6.ipi6_addr;
+ break;
+ }
+#endif
+ default: ASSERT(0);
+ }
return sendmsg (sock->sd, &mesg, 0);
}
@@ -2346,11 +3090,11 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
int status;
/* reset buf to its initial state */
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto))
{
sock->reads.buf = sock->reads.buf_init;
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ else if (proto_is_tcp(sock->info.proto))
{
stream_buf_get_next (&sock->stream_buf, &sock->reads.buf);
}
@@ -2370,10 +3114,15 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
ASSERT (ResetEvent (sock->reads.overlapped.hEvent));
sock->reads.flags = 0;
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto))
{
sock->reads.addr_defined = true;
- sock->reads.addrlen = sizeof (sock->reads.addr);
+#ifdef USE_PF_INET6
+ if (sock->info.proto == PROTO_UDPv6)
+ sock->reads.addrlen = sizeof (sock->reads.addr6);
+ else
+#endif
+ sock->reads.addrlen = sizeof (sock->reads.addr);
status = WSARecvFrom(
sock->sd,
wsabuf,
@@ -2385,7 +3134,7 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
&sock->reads.overlapped,
NULL);
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ else if (proto_is_tcp(sock->info.proto))
{
sock->reads.addr_defined = false;
status = WSARecv(
@@ -2405,8 +3154,14 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
if (!status) /* operation completed immediately? */
{
+#ifdef USE_PF_INET6
+ int addrlen = af_addr_size(sock->info.lsa->local.addr.sa.sa_family);
+ if (sock->reads.addr_defined && sock->reads.addrlen != addrlen)
+ bad_address_length (sock->reads.addrlen, addrlen);
+#else
if (sock->reads.addr_defined && sock->reads.addrlen != sizeof (sock->reads.addr))
bad_address_length (sock->reads.addrlen, sizeof (sock->reads.addr));
+#endif
sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
@@ -2465,12 +3220,22 @@ socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct li
ASSERT (ResetEvent (sock->writes.overlapped.hEvent));
sock->writes.flags = 0;
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto))
{
/* set destination address for UDP writes */
sock->writes.addr_defined = true;
- sock->writes.addr = to->dest.sa;
- sock->writes.addrlen = sizeof (sock->writes.addr);
+#ifdef USE_PF_INET6
+ if (sock->info.proto == PROTO_UDPv6)
+ {
+ sock->writes.addr6 = to->dest.addr.in6;
+ sock->writes.addrlen = sizeof (sock->writes.addr6);
+ }
+ else
+#endif
+ {
+ sock->writes.addr = to->dest.addr.in4;
+ sock->writes.addrlen = sizeof (sock->writes.addr);
+ }
status = WSASendTo(
sock->sd,
@@ -2483,7 +3248,7 @@ socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct li
&sock->writes.overlapped,
NULL);
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ else if (proto_is_tcp(sock->info.proto))
{
/* destination address for TCP writes was established on connection initiation */
sock->writes.addr_defined = false;
@@ -2622,13 +3387,44 @@ socket_finalize (SOCKET s,
if (from)
{
if (ret >= 0 && io->addr_defined)
+#ifdef USE_PF_INET6
+ {
+ /* TODO(jjo): streamline this mess */
+ /* in this func we dont have relevant info about the PF_ of this
+ * endpoint, as link_socket_actual will be zero for the 1st received packet
+ *
+ * Test for inets PF_ possible sizes
+ */
+ switch (io->addrlen)
+ {
+ case sizeof(struct sockaddr_in):
+ case sizeof(struct sockaddr_in6):
+ /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6 */
+ case sizeof(struct sockaddr_in6)-4:
+ break;
+ default:
+ bad_address_length (io->addrlen, af_addr_size(io->addr.sin_family));
+ }
+
+ switch (io->addr.sin_family)
+ {
+ case AF_INET:
+ from->dest.addr.in4 = io->addr;
+ break;
+ case AF_INET6:
+ from->dest.addr.in6 = io->addr6;
+ break;
+ }
+ }
+#else
{
if (io->addrlen != sizeof (io->addr))
bad_address_length (io->addrlen, sizeof (io->addr));
- from->dest.sa = io->addr;
+ from->dest.addr.in4 = io->addr;
}
+#endif
else
- CLEAR (from->dest.sa);
+ CLEAR (from->dest.addr);
}
if (buf)
diff --git a/socket.h b/socket.h
index eef98d1..f9730b3 100644
--- a/socket.h
+++ b/socket.h
@@ -70,7 +70,13 @@ typedef uint16_t packet_size_type;
struct openvpn_sockaddr
{
/*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */
- struct sockaddr_in sa;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in4;
+#ifdef USE_PF_INET6
+ struct sockaddr_in6 in6;
+#endif
+ } addr;
};
/* actual address of remote, based on source address of received packets */
@@ -79,7 +85,17 @@ struct link_socket_actual
/*int dummy;*/ /* add offset to force a bug if dest not explicitly dereferenced */
struct openvpn_sockaddr dest;
#if ENABLE_IP_PKTINFO
- struct in_pktinfo pi;
+ union {
+#ifdef HAVE_IN_PKTINFO
+ struct in_pktinfo in4;
+#endif
+#ifdef IP_RECVDSTADDR
+ struct in_addr in4;
+#endif
+#ifdef USE_PF_INET6
+ struct in6_pktinfo in6;
+#endif
+ } pi;
#endif
};
@@ -199,6 +215,7 @@ struct link_socket
# define SF_TCP_NODELAY (1<<1)
# define SF_PORT_SHARE (1<<2)
# define SF_HOST_RANDOMIZE (1<<3)
+# define SF_GETADDRINFO_DGRAM (1<<4)
unsigned int sockflags;
/* for stream sockets */
@@ -371,6 +388,12 @@ void setenv_link_socket_actual (struct env_set *es,
void bad_address_length (int actual, int expected);
+#ifdef USE_PF_INET6
+/* IPV4_INVALID_ADDR: returned by link_socket_current_remote()
+ * to ease redirect-gateway logic for ipv4 tunnels on ipv6 endpoints
+ */
+#define IPV4_INVALID_ADDR 0xffffffff
+#endif
in_addr_t link_socket_current_remote (const struct link_socket_info *info);
void link_socket_connection_initiated (const struct buffer *buf,
@@ -410,6 +433,14 @@ socket_descriptor_t create_socket_tcp (void);
socket_descriptor_t socket_do_accept (socket_descriptor_t sd,
struct link_socket_actual *act,
const bool nowait);
+/*
+ * proto related
+ */
+bool proto_is_net(int proto);
+bool proto_is_dgram(int proto);
+bool proto_is_udp(int proto);
+bool proto_is_tcp(int proto);
+
#if UNIX_SOCK_SUPPORT
@@ -455,6 +486,11 @@ struct resolve_list {
#define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8)
#define GETADDR_RANDOMIZE (1<<9)
+/* [ab]use flags bits to get socktype info downstream */
+/* TODO(jjo): resolve tradeoff between hackiness|args-overhead */
+#define GETADDR_DGRAM (1<<10)
+#define dnsflags_to_socktype(flags) ((flags & GETADDR_DGRAM) ? SOCK_DGRAM : SOCK_STREAM)
+
in_addr_t getaddr (unsigned int flags,
const char *hostname,
int resolve_retry_seconds,
@@ -472,23 +508,38 @@ in_addr_t getaddr_multi (unsigned int flags,
* Transport protocol naming and other details.
*/
-#define PROTO_UDPv4 0
-#define PROTO_TCPv4_SERVER 1
-#define PROTO_TCPv4_CLIENT 2
-#define PROTO_TCPv4 3
-#define PROTO_N 4
+/*
+ * Use enum's instead of #define to allow for easier
+ * optional proto support
+ */
+enum proto_num {
+ PROTO_NONE, /* catch for uninitialized */
+ PROTO_UDPv4,
+ PROTO_TCPv4_SERVER,
+ PROTO_TCPv4_CLIENT,
+ PROTO_TCPv4,
+#ifdef USE_PF_INET6
+ PROTO_UDPv6,
+ PROTO_TCPv6_SERVER,
+ PROTO_TCPv6_CLIENT,
+ PROTO_TCPv6,
+#endif
+ PROTO_N
+};
int ascii2proto (const char* proto_name);
const char *proto2ascii (int proto, bool display_form);
const char *proto2ascii_all (struct gc_arena *gc);
int proto_remote (int proto, bool remote);
+const char *addr_family_name(int af);
/*
* Overhead added to packets by various protocols.
*/
#define IPv4_UDP_HEADER_SIZE 28
#define IPv4_TCP_HEADER_SIZE 40
-#define IPv6_UDP_HEADER_SIZE 40
+#define IPv6_UDP_HEADER_SIZE 48
+#define IPv6_TCP_HEADER_SIZE 60
extern const int proto_overhead[];
@@ -518,7 +569,7 @@ is_proto_tcp(const int p)
static inline bool
link_socket_proto_connection_oriented (int proto)
{
- return proto == PROTO_TCPv4_SERVER || proto == PROTO_TCPv4_CLIENT;
+ return !proto_is_dgram(proto);
}
static inline bool
@@ -533,7 +584,36 @@ link_socket_connection_oriented (const struct link_socket *sock)
static inline bool
addr_defined (const struct openvpn_sockaddr *addr)
{
- return addr->sa.sin_addr.s_addr != 0;
+ if (!addr) return 0;
+ switch (addr->addr.sa.sa_family) {
+ case AF_INET: return addr->addr.in4.sin_addr.s_addr != 0;
+#ifdef USE_PF_INET6
+ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr);
+#endif
+ default: return 0;
+ }
+}
+static inline bool
+addr_defined_ipi (const struct link_socket_actual *lsa)
+{
+#if ENABLE_IP_PKTINFO
+ if (!lsa) return 0;
+ switch (lsa->dest.addr.sa.sa_family) {
+#ifdef HAVE_IN_PKTINFO
+ case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0;
+#endif
+#ifdef IP_RECVDSTADDR
+ case AF_INET: return lsa->pi.in4.s_addr != 0;
+#endif
+#ifdef USE_PF_INET6
+ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr);
+#endif
+ default: return 0;
+ }
+#else
+ ASSERT(0);
+#endif
+ return false;
}
static inline bool
@@ -545,20 +625,50 @@ link_socket_actual_defined (const struct link_socket_actual *act)
static inline bool
addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
{
- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr;
+ switch(a1->addr.sa.sa_family) {
+ case AF_INET:
+ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr);
+#endif
+ }
+ ASSERT(0);
+ return false;
}
static inline in_addr_t
-addr_host (const struct openvpn_sockaddr *s)
+addr_host (const struct openvpn_sockaddr *addr)
{
- return ntohl (s->sa.sin_addr.s_addr);
+ /*
+ * "public" addr returned is checked against ifconfig for
+ * possible clash: non sense for now given
+ * that we do ifconfig only IPv4
+ */
+#if defined(USE_PF_INET6)
+ if(addr->addr.sa.sa_family != AF_INET)
+ return 0;
+#else
+ ASSERT(addr->addr.sa.sa_family == AF_INET);
+#endif
+ return ntohl (addr->addr.in4.sin_addr.s_addr);
}
static inline bool
addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
{
- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr
- && a1->sa.sin_port == a2->sa.sin_port;
+ switch(a1->addr.sa.sa_family) {
+ case AF_INET:
+ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr
+ && a1->addr.in4.sin_port == a2->addr.in4.sin_port;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr)
+ && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port;
+#endif
+ }
+ ASSERT(0);
+ return false;
}
static inline bool
@@ -571,6 +681,74 @@ addr_match_proto (const struct openvpn_sockaddr *a1,
: addr_port_match (a1, a2);
}
+static inline void
+addr_zero_host(struct openvpn_sockaddr *addr)
+{
+ switch(addr->addr.sa.sa_family) {
+ case AF_INET:
+ addr->addr.in4.sin_addr.s_addr = 0;
+ break;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ memset(&addr->addr.in6.sin6_addr, 0, sizeof (struct in6_addr));
+ break;
+#endif
+ }
+}
+
+static inline void
+addr_copy_sa(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src)
+{
+ dst->addr = src->addr;
+}
+
+static inline void
+addr_copy_host(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src)
+{
+ switch(src->addr.sa.sa_family) {
+ case AF_INET:
+ dst->addr.in4.sin_addr.s_addr = src->addr.in4.sin_addr.s_addr;
+ break;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ dst->addr.in6.sin6_addr = src->addr.in6.sin6_addr;
+ break;
+#endif
+ }
+}
+
+static inline bool
+addr_inet4or6(struct sockaddr *addr)
+{
+ return addr->sa_family == AF_INET || addr->sa_family == AF_INET6;
+}
+
+int addr_guess_family(int proto, const char *name);
+static inline int
+af_addr_size(unsigned short af)
+{
+#if defined(USE_PF_INET6) || defined (USE_PF_UNIX)
+ switch(af) {
+ case AF_INET: return sizeof (struct sockaddr_in);
+#ifdef USE_PF_UNIX
+ case AF_UNIX: return sizeof (struct sockaddr_un);
+#endif
+#ifdef USE_PF_INET6
+ case AF_INET6: return sizeof (struct sockaddr_in6);
+#endif
+ default:
+#if 0
+ /* could be called from socket_do_accept() with empty addr */
+ msg (M_ERR, "Bad address family: %d\n", af);
+ ASSERT(0);
+#endif
+ return 0;
+ }
+#else /* only AF_INET */
+ return sizeof(struct sockaddr_in);
+#endif
+}
+
static inline bool
link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2)
{
@@ -627,14 +805,18 @@ link_socket_verify_incoming_addr (struct buffer *buf,
{
if (buf->len > 0)
{
- if (from_addr->dest.sa.sin_family != AF_INET)
- return false;
- if (!link_socket_actual_defined (from_addr))
- return false;
- if (info->remote_float || !addr_defined (&info->lsa->remote))
- return true;
- if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
- return true;
+ switch (from_addr->dest.addr.sa.sa_family) {
+#ifdef USE_PF_INET6
+ case AF_INET6:
+#endif
+ case AF_INET:
+ if (!link_socket_actual_defined (from_addr))
+ return false;
+ if (info->remote_float || !addr_defined (&info->lsa->remote))
+ return true;
+ if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
+ return true;
+ }
}
return false;
}
@@ -740,7 +922,7 @@ link_socket_read (struct link_socket *sock,
int maxsize,
struct link_socket_actual *from)
{
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
{
int res;
@@ -751,10 +933,10 @@ link_socket_read (struct link_socket *sock,
#endif
return res;
}
- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
+ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
{
/* from address was returned by accept */
- from->dest.sa = sock->info.lsa->actual.dest.sa;
+ addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest);
return link_socket_read_tcp (sock, buf);
}
else
@@ -809,13 +991,14 @@ link_socket_write_udp_posix (struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to);
- if (sock->sockflags & SF_USE_IP_PKTINFO)
+ if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO)
+ && addr_defined_ipi(to))
return link_socket_write_udp_posix_sendmsg (sock, buf, to);
else
#endif
return sendto (sock->sd, BPTR (buf), BLEN (buf), 0,
- (struct sockaddr *) &to->dest.sa,
- (socklen_t) sizeof (to->dest.sa));
+ (struct sockaddr *) &to->dest.addr.sa,
+ (socklen_t) af_addr_size(to->dest.addr.sa.sa_family));
}
static inline int
@@ -846,11 +1029,11 @@ link_socket_write (struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to)
{
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
{
return link_socket_write_udp (sock, buf, to);
}
- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
+ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
{
return link_socket_write_tcp (sock, buf, to);
}
diff --git a/socks.c b/socks.c
index 8287274..949d256 100644
--- a/socks.c
+++ b/socks.c
@@ -299,9 +299,9 @@ recv_socks_reply (socket_descriptor_t sd,
if (addr != NULL)
{
- addr->sa.sin_family = AF_INET;
- addr->sa.sin_addr.s_addr = htonl (INADDR_ANY);
- addr->sa.sin_port = htons (0);
+ addr->addr.in4.sin_family = AF_INET;
+ addr->addr.in4.sin_addr.s_addr = htonl (INADDR_ANY);
+ addr->addr.in4.sin_port = htons (0);
}
while (len < 4 + alen + 2)
@@ -388,8 +388,8 @@ recv_socks_reply (socket_descriptor_t sd,
/* ATYP == 1 (IP V4 address) */
if (atyp == '\x01' && addr != NULL)
{
- memcpy (&addr->sa.sin_addr, buf + 4, sizeof (addr->sa.sin_addr));
- memcpy (&addr->sa.sin_port, buf + 8, sizeof (addr->sa.sin_port));
+ memcpy (&addr->addr.in4.sin_addr, buf + 4, sizeof (addr->addr.in4.sin_addr));
+ memcpy (&addr->addr.in4.sin_port, buf + 8, sizeof (addr->addr.in4.sin_port));
}
@@ -507,8 +507,8 @@ socks_process_incoming_udp (struct buffer *buf,
if (atyp != 1) /* ATYP == 1 (IP V4) */
goto error;
- buf_read (buf, &from->dest.sa.sin_addr, sizeof (from->dest.sa.sin_addr));
- buf_read (buf, &from->dest.sa.sin_port, sizeof (from->dest.sa.sin_port));
+ buf_read (buf, &from->dest.addr.in4.sin_addr, sizeof (from->dest.addr.in4.sin_addr));
+ buf_read (buf, &from->dest.addr.in4.sin_port, sizeof (from->dest.addr.in4.sin_port));
return;
@@ -540,8 +540,8 @@ socks_process_outgoing_udp (struct buffer *buf,
buf_write_u16 (&head, 0); /* RSV = 0 */
buf_write_u8 (&head, 0); /* FRAG = 0 */
buf_write_u8 (&head, '\x01'); /* ATYP = 1 (IP V4) */
- buf_write (&head, &to->dest.sa.sin_addr, sizeof (to->dest.sa.sin_addr));
- buf_write (&head, &to->dest.sa.sin_port, sizeof (to->dest.sa.sin_port));
+ buf_write (&head, &to->dest.addr.in4.sin_addr, sizeof (to->dest.addr.in4.sin_addr));
+ buf_write (&head, &to->dest.addr.in4.sin_port, sizeof (to->dest.addr.in4.sin_port));
return 10;
}
diff --git a/syshead.h b/syshead.h
index 30ff556..9dbb501 100644
--- a/syshead.h
+++ b/syshead.h
@@ -28,6 +28,10 @@
/*
* Only include if not during configure
*/
+#ifdef WIN32
+/* USE_PF_INET6: win32 ipv6 exists only after 0x0501 (XP) */
+#define WINVER 0x0501
+#endif
#ifndef PACKAGE_NAME
#include "config.h"
#endif
@@ -339,6 +343,9 @@
#ifdef WIN32
#include <iphlpapi.h>
#include <wininet.h>
+/* The following two headers are needed of USE_PF_INET6 */
+#include <winsock2.h>
+#include <ws2tcpip.h>
#endif
#ifdef HAVE_SYS_MMAN_H
@@ -383,9 +390,10 @@
#endif
/*
- * Does this platform support linux-style IP_PKTINFO?
+ * Does this platform support linux-style IP_PKTINFO
+ * or bsd-style IP_RECVDSTADDR ?
*/
-#if defined(ENABLE_MULTIHOME) && defined(HAVE_IN_PKTINFO) && defined(IP_PKTINFO) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
+#if defined(ENABLE_MULTIHOME) && ((defined(HAVE_IN_PKTINFO)&&defined(IP_PKTINFO)) || defined(IP_RECVDSTADDR)) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
#define ENABLE_IP_PKTINFO 1
#else
#define ENABLE_IP_PKTINFO 0
diff --git a/tun.c b/tun.c
index 59e87dc..f8ebbb5 100644
--- a/tun.c
+++ b/tun.c
@@ -1697,7 +1697,9 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
strerror(errno));
}
+#ifdef IFF_MULTICAST /* openbsd 4.x doesn't have this */
info.flags |= IFF_MULTICAST;
+#endif
if (ioctl (tt->fd, TUNSIFINFO, &info) < 0) {
msg (M_WARN | M_ERRNO, "Can't set interface info: %s",
diff --git a/win32.h b/win32.h
index b6a162e..f2d9aec 100644
--- a/win32.h
+++ b/win32.h
@@ -195,7 +195,10 @@ struct overlapped_io {
DWORD flags;
int status;
bool addr_defined;
- struct sockaddr_in addr;
+ union {
+ struct sockaddr_in addr;
+ struct sockaddr_in6 addr6;
+ };
int addrlen;
struct buffer buf_init;
struct buffer buf;