Hi, To allow seamless roaming of our robots at willowgarage (http://willowgarage.com), I have put together a patch that allows TLS connections to float. I would like to put this patch up for critique and possible inclusion into mainline openvpn. The general approach has been to prefix a few bytes at the start of each packet which contain an opcode and a unique identifier (random number) for the session. That identifier is used instead of the IP address for determining which connection the packet belongs to.
Regards, Blaise --- a/configure.ac +++ b/configure.ac @@ -129,6 +129,12 @@ [FRAGMENT="yes"] ) +AC_ARG_ENABLE(floating-tls, + [ --disable-floating-tls Disable floating tls support (--floating-tls)], + [FLOATING_TLS="$enableval"], + [FLOATING_TLS="yes"] +) + AC_ARG_ENABLE(multihome, [ --disable-multihome Disable multi-homed UDP server support (--multihome)], [MULTIHOME="$enableval"], @@ -791,6 +797,11 @@ AC_DEFINE(ENABLE_HTTP_PROXY, 1, [Enable HTTP proxy support]) fi +dnl compile --floating-tls option +if test "$FLOATING_TLS" = "yes"; then + AC_DEFINE(FLOATING_TLS, 1, [Enable floating-tls UDP server capability]) +fi + dnl compile --multihome option if test "$MULTIHOME" = "yes"; then AC_DEFINE(ENABLE_MULTIHOME, 1, [Enable multi-homed UDP server capability]) --- a/forward.c +++ b/forward.c @@ -39,6 +39,10 @@ #include "occ-inline.h" #include "ping-inline.h" +#ifdef FLOATING_TLS +#include "ssl.h" +#endif + /* show event wait debugging info */ #ifdef ENABLE_DEBUG @@ -834,7 +838,11 @@ * * Also, update the persisted version of our packet-id. */ - if (!TLS_MODE (c)) + if (!TLS_MODE (c) +#ifdef FLOATING_TLS + || c->options.floating_tls +#endif + ) link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.from, NULL, c->c2.es); /* reset packet received timer */ @@ -1088,6 +1096,21 @@ /* If Socks5 over UDP, prepend header */ socks_preprocess_outgoing_link (c, &to_addr, &size_delta); #endif + +#ifdef FLOATING_TLS + if (c->c2.link_socket->info.proto == PROTO_UDPv4 && + c->options.floating_tls && + c->options.tls_client) + { + if (c->floating_tls_prefix == 0) + RAND_bytes((uint8_t *) &c->floating_tls_prefix, sizeof(c->floating_tls_prefix)); + + struct buffer *buf = &c->c2.to_link; + uint8_t opcode = FLOATING_TLS_OPCODE | sizeof(c->floating_tls_prefix); + ASSERT (buf_write_prepend (buf, &c->floating_tls_prefix, sizeof (c->floating_tls_prefix))); + ASSERT (buf_write_prepend (buf, &opcode, sizeof (opcode))); + } +#endif /* Send packet */ size = link_socket_write (c->c2.link_socket, &c->c2.to_link, --- a/init.c +++ b/init.c @@ -1826,6 +1826,10 @@ to.disable_occ = !options->occ; #endif +#ifdef FLOATING_TLS + to.floating_tls = options->floating_tls; +#endif + to.verify_command = options->tls_verify; to.verify_x509name = options->tls_remote; to.crl_file = options->crl_file; --- a/mudp.c +++ b/mudp.c @@ -31,6 +31,10 @@ #include "memdbg.h" +#ifdef FLOATING_TLS +#include "ssl.h" +#endif + /* * Get a client instance based on real address. If * the instance doesn't exist, create it while @@ -44,8 +48,36 @@ struct mroute_addr real; struct multi_instance *mi = NULL; struct hash *hash = m->hash; + bool ret = false; + +#ifdef FLOATING_TLS + // Check if this is a floating-tls packet + if (m->top.c2.buf.len > 0) + { + uint8_t c = *BPTR (&m->top.c2.buf); + if ((c & FLOATING_TLS_OPCODE_MASK) == FLOATING_TLS_OPCODE) + { + int len = c & FLOATING_TLS_LENGTH_MASK; + uint8_t *id = BPTR (&m->top.c2.buf) + 1; + + if (buf_advance(&m->top.c2.buf, len + 1)) + { + int i; + real.type = MR_ADDR_IPV4 | MR_WITH_PORT; + real.netbits = 0; + real.len = len; + memcpy (real.addr, id, real.len); + ret = true; + } + } + } +#endif + + // Not a floating-tls packet + if (!ret) + ret = mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true); - if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true)) + if (ret) { struct hash_element *he; const uint32_t hv = hash_value (hash, &real); --- a/openvpn.h +++ b/openvpn.h @@ -484,6 +484,10 @@ /* persistent across SIGHUP */ struct context_persist persist; +#ifdef FLOATING_TLS + uint64_t floating_tls_prefix; +#endif + /* level 0 context contains data related to once-per OpenVPN instantiation events such as daemonization */ --- a/options.c +++ b/options.c @@ -126,6 +126,7 @@ " Set n=\"infinite\" to retry indefinitely.\n" "--float : Allow remote to change its IP address/port, such as through\n" " DHCP (this is the default if --remote is not used).\n" + "--floating-tls : Allows floating in multi-tls sessions.\n" "--ipchange cmd : Execute shell command cmd on remote ip address initial\n" " setting or change -- execute as: cmd ip-address port#\n" "--port port : TCP/UDP port # for both local and remote.\n" @@ -1334,6 +1335,10 @@ SHOW_BOOL (tls_exit); SHOW_STR (tls_auth_file); + +#ifdef FLOATING_TLS + SHOW_BOOL (floating_tls); +#endif #endif #endif @@ -1909,6 +1914,9 @@ MUST_BE_UNDEF (transition_window); MUST_BE_UNDEF (tls_auth_file); MUST_BE_UNDEF (single_session); +#ifdef FLOATING_TLS + MUST_BE_UNDEF (floating_tls); +#endif MUST_BE_UNDEF (tls_exit); MUST_BE_UNDEF (crl_file); MUST_BE_UNDEF (key_method); @@ -2339,6 +2347,11 @@ buf_printf (&out, ",no-iv"); } +#ifdef FLOATING_TLS + if (o->floating_tls) + buf_printf (&out, ",floating-tls"); +#endif + #ifdef USE_SSL /* * SSL Options @@ -5413,6 +5426,13 @@ VERIFY_PERMISSION (OPT_P_GENERAL); options->single_session = true; } +#ifdef FLOATING_TLS + else if (streq (p[0], "floating-tls")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->floating_tls = true; + } +#endif else if (streq (p[0], "tls-exit")) { VERIFY_PERMISSION (OPT_P_GENERAL); --- a/options.h +++ b/options.h @@ -168,6 +168,10 @@ bool genkey; #endif +#ifdef FLOATING_TLS + bool floating_tls; +#endif + /* Networking parms */ struct connection_entry ce; --- a/ssl.c +++ b/ssl.c @@ -4160,7 +4160,12 @@ #ifdef ENABLE_DEF_AUTH && !ks->auth_deferred #endif - && link_socket_actual_match (from, &ks->remote_addr)) + && ( +#ifdef FLOATING_TLS + multi->opt.floating_tls || +#endif + link_socket_actual_match (from, &ks->remote_addr) + )) { /* return appropriate data channel decrypt key in opt */ opt->key_ctx_bi = &ks->key; @@ -4403,7 +4408,11 @@ /* * Verify remote IP address */ - if (!new_link && !link_socket_actual_match (&ks->remote_addr, from)) + if (!new_link +#ifdef FLOATING_TLS + && !multi->opt.floating_tls +#endif + && !link_socket_actual_match (&ks->remote_addr, from)) { msg (D_TLS_ERRORS, "TLS Error: Received control packet from unexpected IP addr: %s", print_link_socket_actual (from, &gc)); @@ -4468,7 +4477,11 @@ ks->remote_addr = *from; ++multi->n_sessions; } - else if (!link_socket_actual_match (&ks->remote_addr, from)) + else if ( +#ifdef FLOATING_TLS + !multi->opt.floating_tls && +#endif + !link_socket_actual_match (&ks->remote_addr, from)) { msg (D_TLS_ERRORS, "TLS Error: Existing session control channel packet from unknown IP address: %s", --- a/ssl.h +++ b/ssl.h @@ -221,6 +221,13 @@ #define P_FIRST_OPCODE 1 #define P_LAST_OPCODE 8 +/* Extra opcodes for floating TLS */ +#ifdef FLOATING_TLS +#define FLOATING_TLS_OPCODE_MASK 0xF0 +#define FLOATING_TLS_OPCODE 0xF0 +#define FLOATING_TLS_LENGTH_MASK 0x0F +#endif + /* key negotiation states */ #define S_ERROR -1 #define S_UNDEF 0 @@ -416,6 +423,10 @@ int key_method; bool replay; bool single_session; +#ifdef FLOATING_TLS + bool floating_tls; +#endif + #ifdef ENABLE_OCC bool disable_occ; #endif --- a/openvpn.8 +++ b/openvpn.8 @@ -573,6 +573,10 @@ option. .\"********************************************************* .TP +.B \-\-floating-tls +Allows tls connections to float. +.\"********************************************************* +.TP .B \-\-ipchange cmd Execute shell command .B cmd