diff options
author | Antonio Quartulli | 2022-08-05 08:45:55 +0200 |
---|---|---|
committer | Gert Doering | 2022-08-05 16:03:33 +0200 |
commit | a5b4bad46978a01162fb820ea25594d6333aa9db (patch) | |
tree | 43e94f5d9e173da8f251712940a8804f669b76e8 /src/openvpn/multi.c | |
parent | 78c02dd12bcfe7c4b4e96ea534b39ade9ee2c4d6 (diff) | |
download | openvpn-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.c | 236 |
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) { |