Hi, our setup needs openvpn UDP/TLS tunnels with dynamic client IP addresses, so I implemented a fix for the bug #49 that has been open for over two years.
The patch is for version 2.2.2 as I had trouble compiling the 2.3.x tarball from openvpn.net. As the solution is rather simple (just two small utility functions in mudp.c) I'd guess it could be comfortambly migrated to 2.3.x. Basically what the fix does is the following: incoming data channel UDP packets from an unknown IP are matched against existing UDP/TLS sessions, and if the packet passes the HMAC authentication against an existing TLS context we know the client IP has changed and the session state will be instantly updated accordingly. I have tested this fix to some extent, and the IP handover works impressively smoothly in my test setup where I randomly switch between two routes from client to server. Dynamic client IP's are enabled/disabled with --float in the server side. Please feel free to contact me for any questions etc. Best regards, Mikko Vainikainen <mikko.vainikai...@konecranes.com>
From c00381f1b53c5169dba1b2e4bf22bb6e50e4eaea Mon Sep 17 00:00:00 2001 From: Mikko Vainikainen <mikko.vainikai...@konecranes.com> Date: Fri, 8 Mar 2013 12:00:52 +0200 Subject: [PATCH] Fixes bug #49, support for dynamic client IP over UDP/TLS. --- mudp.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/mudp.c b/mudp.c index a478b29..ef5bb3c 100644 --- a/mudp.c +++ b/mudp.c @@ -30,6 +30,161 @@ #include "forward-inline.h" #include "memdbg.h" +#include "socket.h" + + +/* + * Helper function for authenticating packets against existing TLS context. + */ + +int authenticate_tls_packet(struct multi_context* m, + struct context* ctx, + struct buffer* buf, + struct tls_multi* multi) +{ + int i; + int op; + int key_id; + int status; + struct link_socket_actual* from = &m->top.c2.from; + + if (buf-> len <= 0) return 0; + + { + uint8_t c = *BPTR(buf); + op = c >> P_OPCODE_SHIFT; + key_id = c & P_KEY_ID_MASK; + } + + /* + * we are only interested in data channel packets + */ + + if (op != P_DATA_V1) return 0; + + for (i = 0; i < KEY_SCAN_SIZE; ++i) + { + + struct key_state* ks = multi->key_scan[i]; + struct key_ctx* kc = &ks->key.decrypt; + int hmac_len; + int in_hmac_len; + uint8_t local_hmac[MAX_HMAC_KEY_LENGTH]; + + if (!DECRYPT_KEY_ENABLED(multi, ks) || + key_id != ks->key_id || + !ks->authenticated) + { + return 0; + } + +#ifdef ENABLE_DEF_AUTH + + if (ks->auth_deferred) return 0; + +#endif + + if (!kc->hmac) return 0; + + /* + * authenticate the packet with HMAC + */ + + HMAC_Init_ex(kc->hmac, NULL, 0, NULL, NULL); + hmac_len = HMAC_size(kc->hmac); + if (buf->len - 1 < hmac_len) return 0; + HMAC_Update(kc->hmac, BPTR(buf) + hmac_len + 1, BLEN(buf) - hmac_len - 1); + HMAC_Final(kc->hmac, local_hmac, (unsigned int*)&in_hmac_len); + if (hmac_len != in_hmac_len) return 0; + if (memcmp(local_hmac, BPTR(buf) + 1, hmac_len)) return 0; + + /* + * HMAC authentication successful, change the IP at + * TLS layer + */ + + { + struct gc_arena gc = gc_new(); + msg(D_TLS_DEBUG_LOW, "%s/%s IP changed: %s", + tls_common_name(multi, 0), + print_link_socket_actual(&ks->remote_addr, &gc), + print_link_socket_actual(from, &gc)); + memcpy(&ks->remote_addr, from, sizeof(*from)); + gc_free(&gc); + } + return 1; + + } + + return 0; +} + + +/* + * Utility function for finding an existing TLS session + * with different IP + */ + +struct hash_element* detect_ip_change(struct multi_context* m, + struct hash* hash, + struct hash_bucket* bucket, + uint32_t hv, + struct mroute_addr* real) +{ + struct hash_iterator it; + struct hash_element* pe; + struct hash_element* retval = NULL; + struct buffer* buf = &m->top.c2.buf; + + /* + * match the incoming packet against all existing sessions + */ + + hash_iterator_init(hash, &it); + while ((pe = hash_iterator_next(&it)) != NULL) + { + + /* + * get TLS context for current item + */ + + struct multi_instance* mi = pe->value; + struct context* ctx = &mi->context; + struct tls_multi* multi = ctx->c2.tls_multi; + + /* + * we are only interested in TUN devices + */ + + if (ctx->c1.tuntap->type != DEV_TYPE_TUN) continue; + + /* + * authenticate the packet, and continue search if unsuccessful + */ + + if (!authenticate_tls_packet(m, ctx, buf, multi)) continue; + + /* + * found a match, update the IP at UDP layer + */ + + hash_remove(hash, &mi->real); + if (mi->did_iter) hash_remove(m->iter, &mi->real); + memcpy(&mi->real, real, sizeof(*real)); + hash_add_fast(hash, bucket, &mi->real, hv, mi); + if (mi->did_iter) hash_add(m->iter, &mi->real, mi, false); + + /* + * return the match found + */ + + retval = pe; + break; + } + hash_iterator_free(&it); + return retval; +} + /* * Get a client instance based on real address. If @@ -53,6 +208,17 @@ multi_get_create_instance_udp (struct multi_context *m) he = hash_lookup_fast (hash, bucket, &real, hv); + /* + * if --float was specified search for an existing session + * with different IP + */ + + if (!he && m->top.options.ce.remote_float) he = detect_ip_change(m, + hash, + bucket, + hv, + &real); + if (he) { mi = (struct multi_instance *) he->value; -- 1.7.10.4