@@ -218,8 +218,7 @@
static struct multi_instance *
handle_connection_attempt(struct multi_context *m,
struct link_socket *sock,
- struct mroute_addr *real,
- struct hash_bucket *bucket)
+ struct mroute_addr *real)
{
struct hash *hash = m->hash;
struct tls_pre_decrypt_state state = { 0 };
@@ -250,7 +249,9 @@
if (mi)
{
const uint64_t hv = hash_value(hash, real);
+ struct hash_bucket *bucket = hash_bucket(hash, hv);
hash_add_fast(hash, bucket, &mi->real, hv, mi);
+
mi->did_real_hash = true;
multi_assign_peer_id(m, mi);
@@ -299,6 +300,19 @@
return NULL;
}
+struct multi_instance *
+multi_get_instance_udp_control(struct multi_context *m, struct link_socket *sock)
+{
+ struct mroute_addr real = { 0 };
+ real.proto = sock->info.proto;
+
+ if (mroute_extract_openvpn_sockaddr(&real, &m->top.c2.from.dest, true) && m->top.c2.buf.len > 0)
+ {
+ return multi_get_instance_udp_real(m, &real);
+ }
+
+ return NULL;
+}
/**
* Get a client instance based on real address. If
@@ -306,72 +320,101 @@
* maintaining real address hash table atomicity.
*/
struct multi_instance *
-multi_get_create_instance_udp(struct multi_context *m, bool *floated, struct link_socket *sock)
+multi_get_instance_udp_data(struct multi_context *m, bool *floated, struct mroute_addr *real, struct link_socket *sock)
{
- struct gc_arena gc = gc_new();
- struct mroute_addr real = { 0 };
struct multi_instance *mi = NULL;
- struct hash *hash = m->hash;
- real.proto = sock->info.proto;
- if (mroute_extract_openvpn_sockaddr(&real, &m->top.c2.from.dest, true) && m->top.c2.buf.len > 0)
+ uint8_t *ptr = BPTR(&m->top.c2.buf);
+ uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
+ bool v2 = (op == P_DATA_V2) && (m->top.c2.buf.len >= (1 + 3));
+ bool peer_id_disabled = false;
+
+ /* make sure buffer has enough length to read opcode (1 byte) and peer-id (3 bytes) */
+ if (v2)
{
- const uint64_t hv = hash_value(hash, &real);
- struct hash_bucket *bucket = hash_bucket(hash, hv);
- uint8_t *ptr = BPTR(&m->top.c2.buf);
- uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
- bool v2 = (op == P_DATA_V2) && (m->top.c2.buf.len >= (1 + 3));
- bool peer_id_disabled = false;
+ uint32_t peer_id = ((uint32_t)ptr[1] << 16) | ((uint32_t)ptr[2] << 8) | ((uint32_t)ptr[3]);
+ peer_id_disabled = (peer_id == MAX_PEER_ID);
- /* make sure buffer has enough length to read opcode (1 byte) and peer-id (3 bytes) */
- if (v2)
+ if (!peer_id_disabled && (peer_id < m->max_clients) && (m->instances[peer_id]))
{
- uint32_t peer_id = ((uint32_t)ptr[1] << 16) | ((uint32_t)ptr[2] << 8) | ((uint32_t)ptr[3]);
- peer_id_disabled = (peer_id == MAX_PEER_ID);
-
- if (!peer_id_disabled && (peer_id < m->max_clients) && m->instances[peer_id])
+ /* Floating on TCP will never be possible, so ensure we only process
+ * UDP clients */
+ if (m->instances[peer_id]->context.c2.link_sockets[0]->info.proto
+ == sock->info.proto)
{
- /* Floating on TCP will never be possible, so ensure we only process
- * UDP clients */
- if (m->instances[peer_id]->context.c2.link_sockets[0]->info.proto
- == sock->info.proto)
- {
- mi = m->instances[peer_id];
- *floated = !link_socket_actual_match(&mi->context.c2.from, &m->top.c2.from);
+ mi = m->instances[peer_id];
+ *floated = !link_socket_actual_match(&mi->context.c2.from, &m->top.c2.from);
- if (*floated)
- {
- /* reset prefix, since here we are not sure peer is the one it claims to be
- */
- ungenerate_prefix(mi);
- msg(D_MULTI_MEDIUM, "Float requested for peer %" PRIu32 " to %s", peer_id,
- mroute_addr_print(&real, &gc));
- }
+ if (*floated)
+ {
+ /* reset prefix, since here we are not sure peer is the one it claims to be
+ */
+ ungenerate_prefix(mi);
+ struct gc_arena gc = gc_new();
+ msg(D_MULTI_MEDIUM, "Float requested for peer %" PRIu32 " to %s", peer_id,
+ mroute_addr_print(real, &gc));
+ gc_free(&gc);
}
+ return mi;
}
}
- if (!v2 || peer_id_disabled)
- {
- mi = multi_get_instance_udp_real(m, &real);
- }
+ }
+ if (!v2 || peer_id_disabled)
+ {
+ return multi_get_instance_udp_real(m, real);
+ }
+ return NULL;
+}
- /* we have no existing multi instance for this connection */
- if (!mi)
- {
- mi = handle_connection_attempt(m, sock, &real, bucket);
- }
-
-#ifdef ENABLE_DEBUG
- if (check_debug_level(D_MULTI_DEBUG))
- {
- const char *status = mi ? "[ok]" : "[failed]";
-
- dmsg(D_MULTI_DEBUG, "GET INST BY REAL: %s %s", mroute_addr_print(&real, &gc), status);
- }
-#endif
+struct multi_instance *
+multi_get_create_instance_udp(struct multi_context *m, bool *floated, struct link_socket *sock)
+{
+ /* If the buffer is empty, the packet has no op code and can be neither
+ * a (valid) data nor control packet */
+ if (m->top.c2.buf.len <= 0)
+ {
+ return NULL;
}
- gc_free(&gc);
+ uint8_t *ptr = BPTR(&m->top.c2.buf);
+ uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
+
+ struct mroute_addr real = { 0 };
+ real.proto = sock->info.proto;
+
+ if (!mroute_extract_openvpn_sockaddr(&real, &m->top.c2.from.dest, true))
+ {
+ return NULL;
+ }
+
+ struct multi_instance *mi = NULL;
+ if (op == P_DATA_V1 || op == P_DATA_V2)
+ {
+ mi = multi_get_instance_udp_data(m, floated, &real, sock);
+ }
+ else
+ {
+ mi = multi_get_instance_udp_control(m, sock);
+
+ /* we have no existing multi instance for this connection, control
+ * packets can create a session. Data packets cannot */
+ if (!mi)
+ {
+ mi = handle_connection_attempt(m, sock, &real);
+ }
+ }
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level(D_MULTI_DEBUG))
+ {
+ struct gc_arena gc = gc_new();
+ const char *status = mi ? "[ok]" : "[failed]";
+
+ dmsg(D_MULTI_DEBUG, "GET INST BY REAL/SID: %s %s", mroute_addr_print(&real, &gc), status);
+ gc_free(&gc);
+ }
+#endif
+
ASSERT(!(mi && mi->halt));
return mi;
}