aboutsummaryrefslogtreecommitdiff
path: root/src/openvpn/multi.c
diff options
context:
space:
mode:
authorAntonio Quartulli2022-08-05 08:45:55 +0200
committerGert Doering2022-08-05 16:03:33 +0200
commita5b4bad46978a01162fb820ea25594d6333aa9db (patch)
tree43e94f5d9e173da8f251712940a8804f669b76e8 /src/openvpn/multi.c
parent78c02dd12bcfe7c4b4e96ea534b39ade9ee2c4d6 (diff)
downloadopenvpn-a5b4bad46978a01162fb820ea25594d6333aa9db.zip
openvpn-a5b4bad46978a01162fb820ea25594d6333aa9db.tar.gz
dco: implement dco support for p2mp/server code path
This change introduces ovpn-dco support along the p2mp/server code path. Some code seems to be duplicate of the p2p version, but details are different, so it couldn't be shared. Signed-off-by: Antonio Quartulli <a@unstable.cc> Acked-by: Heiko Hund <heiko@ist.eigentlich.net> Acked-by: Gert Doering <gert@greenie.muc.de> Message-Id: <20220805064555.13385-1-a@unstable.cc> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24811.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
Diffstat (limited to 'src/openvpn/multi.c')
-rw-r--r--src/openvpn/multi.c236
1 files changed, 195 insertions, 41 deletions
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index c72575a..dcf4438 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -51,6 +51,7 @@
#include "crypto_backend.h"
#include "ssl_util.h"
+#include "dco.h"
/*#define MULTI_DEBUG_EVENT_LOOP*/
@@ -519,6 +520,9 @@ multi_del_iroutes(struct multi_context *m,
{
const struct iroute *ir;
const struct iroute_ipv6 *ir6;
+
+ dco_delete_iroutes(m, mi);
+
if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
{
for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
@@ -1224,16 +1228,22 @@ multi_learn_in_addr_t(struct multi_context *m,
addr.netbits = (uint8_t) netbits;
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* "primary" is the VPN ifconfig address of the peer and already
+ * known to DCO, so only install "extra" iroutes (primary = false)
+ */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
static struct multi_instance *
@@ -1257,16 +1267,22 @@ multi_learn_in6_addr(struct multi_context *m,
mroute_addr_mask_host_bits( &addr );
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* "primary" is the VPN ifconfig address of the peer and already
+ * known to DCO, so only install "extra" iroutes (primary = false)
+ */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
/*
@@ -1765,6 +1781,15 @@ multi_client_set_protocol_options(struct context *c)
tls_multi->use_peer_id = true;
o->use_peer_id = true;
}
+ else if (dco_enabled(o))
+ {
+ msg(M_INFO, "Client does not support DATA_V2. Data channel offloaing "
+ "requires DATA_V2. Dropping client.");
+ auth_set_client_reason(tls_multi, "Data channel negotiation "
+ "failed (missing DATA_V2)");
+ return false;
+ }
+
if (proto & IV_PROTO_REQUEST_PUSH)
{
c->c2.push_request_received = true;
@@ -2401,9 +2426,37 @@ multi_client_connect_late_setup(struct multi_context *m,
}
/* Generate data channel keys only if setting protocol options
* has not failed */
- else if (!multi_client_generate_tls_keys(&mi->context))
+ else
{
- mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ if (dco_enabled(&mi->context.options))
+ {
+ int ret = dco_multi_add_new_peer(m, mi);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot add peer to DCO: %s (%d)", strerror(-ret), ret);
+ mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ }
+
+ if (mi->context.options.ping_send_timeout || mi->context.c2.frame.mss_fix)
+ {
+ int ret = dco_set_peer(&mi->context.c1.tuntap->dco,
+ mi->context.c2.tls_multi->peer_id,
+ mi->context.options.ping_send_timeout,
+ mi->context.options.ping_rec_timeout,
+ mi->context.c2.frame.mss_fix);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot set parameters for DCO peer (id=%u): %s",
+ mi->context.c2.tls_multi->peer_id, strerror(-ret));
+ mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ }
+ }
+ }
+
+ if (!multi_client_generate_tls_keys(&mi->context))
+ {
+ mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ }
}
/* send push reply if ready */
@@ -2661,6 +2714,14 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
(*cur_handler_index)++;
}
+ /* Check if we have forbidding options in the current mode */
+ if (dco_enabled(&mi->context.options)
+ && !dco_check_option_conflict(D_MULTI_ERRORS, &mi->context.options))
+ {
+ msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to incompatible DCO options");
+ cc_succeeded = false;
+ }
+
if (cc_succeeded)
{
multi_client_connect_late_setup(m, mi, *option_types_found);
@@ -3080,6 +3141,124 @@ done:
}
/*
+ * Called when an instance should be closed due to the
+ * reception of a soft signal.
+ */
+void
+multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
+{
+ remap_signal(&mi->context);
+ set_prefix(mi);
+ print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
+ clear_prefix();
+ multi_close_instance(m, mi, false);
+}
+
+#if (defined(ENABLE_DCO) && defined(TARGET_LINUX)) || defined(ENABLE_MANAGEMENT)
+static void
+multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
+{
+ mi->context.sig->signal_received = sig;
+ multi_close_instance_on_signal(m, mi);
+}
+#endif
+
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+static void
+process_incoming_dco_packet(struct multi_context *m, struct multi_instance *mi,
+ dco_context_t *dco)
+{
+ if (BLEN(&dco->dco_packet_in) < 1)
+ {
+ msg(D_DCO, "Received too short packet for peer %d",
+ dco->dco_message_peer_id);
+ goto done;
+ }
+
+ uint8_t *ptr = BPTR(&dco->dco_packet_in);
+ uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
+ if ((op == P_DATA_V1) || (op == P_DATA_V2))
+ {
+ msg(D_DCO, "DCO: received data channel packet for peer %d",
+ dco->dco_message_peer_id);
+ goto done;
+ }
+
+ struct buffer orig_buf = mi->context.c2.buf;
+ mi->context.c2.buf = dco->dco_packet_in;
+
+ multi_process_incoming_link(m, mi, 0);
+
+ mi->context.c2.buf = orig_buf;
+
+done:
+ buf_init(&dco->dco_packet_in, 0);
+}
+
+static void
+process_incoming_del_peer(struct multi_context *m, struct multi_instance *mi,
+ dco_context_t *dco)
+{
+ const char *reason = "ovpn-dco: unknown reason";
+ switch (dco->dco_del_peer_reason)
+ {
+ case OVPN_DEL_PEER_REASON_EXPIRED:
+ reason = "ovpn-dco: ping expired";
+ break;
+
+ case OVPN_DEL_PEER_REASON_TRANSPORT_ERROR:
+ reason = "ovpn-dco: transport error";
+ break;
+
+ case OVPN_DEL_PEER_REASON_USERSPACE:
+ /* This very likely ourselves but might be another process, so
+ * still process it */
+ reason = "ovpn-dco: userspace request";
+ break;
+ }
+
+ /* When kernel already deleted the peer, the socket is no longer
+ * installed and we don't need to cleanup the state in the kernel */
+ mi->context.c2.tls_multi->dco_peer_added = false;
+ mi->context.sig->signal_text = reason;
+ multi_signal_instance(m, mi, SIGTERM);
+}
+
+bool
+multi_process_incoming_dco(struct multi_context *m)
+{
+ dco_context_t *dco = &m->top.c1.tuntap->dco;
+
+ struct multi_instance *mi = NULL;
+
+ int ret = dco_do_read(&m->top.c1.tuntap->dco);
+
+ int peer_id = dco->dco_message_peer_id;
+
+ if ((peer_id >= 0) && (peer_id < m->max_clients) && (m->instances[peer_id]))
+ {
+ mi = m->instances[peer_id];
+ if (dco->dco_message_type == OVPN_CMD_PACKET)
+ {
+ process_incoming_dco_packet(m, mi, dco);
+ }
+ else if (dco->dco_message_type == OVPN_CMD_DEL_PEER)
+ {
+ process_incoming_del_peer(m, mi, dco);
+ }
+ }
+ else
+ {
+ msg(D_DCO, "Received packet for peer-id unknown to OpenVPN: %d", peer_id);
+ }
+
+ dco->dco_message_type = 0;
+ dco->dco_message_peer_id = -1;
+ return ret > 0;
+}
+#endif /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
+
+/*
* Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
* i.e. client -> server direction.
*/
@@ -3641,32 +3820,11 @@ multi_process_signal(struct multi_context *m)
}
/*
- * Called when an instance should be closed due to the
- * reception of a soft signal.
- */
-void
-multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
-{
- remap_signal(&mi->context);
- set_prefix(mi);
- print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
- clear_prefix();
- multi_close_instance(m, mi, false);
-}
-
-/*
* Management subsystem callbacks
*/
#ifdef ENABLE_MANAGEMENT
static void
-multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
-{
- mi->context.sig->signal_received = sig;
- multi_close_instance_on_signal(m, mi);
-}
-
-static void
management_callback_status(void *arg, const int version, struct status_output *so)
{
struct multi_context *m = (struct multi_context *) arg;
@@ -3755,10 +3913,6 @@ management_delete_event(void *arg, event_t event)
}
}
-#endif /* ifdef ENABLE_MANAGEMENT */
-
-#ifdef ENABLE_MANAGEMENT
-
static struct multi_instance *
lookup_by_cid(struct multi_context *m, const unsigned long cid)
{