| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| #include <climits> |
| #include <string> |
| |
| #include "tscore/ink_config.h" |
| #include "records/I_RecHttp.h" |
| #include "tscore/Diags.h" |
| |
| #include "P_QUICNetVConnection.h" |
| #include "P_QUICPacketHandler.h" |
| #include "P_Net.h" |
| #include "InkAPIInternal.h" // Added to include the quic_hook definitions |
| #include "Log.h" |
| |
| #include "P_SSLNextProtocolSet.h" |
| #include "QUICMultiCertConfigLoader.h" |
| #include "QUICTLS.h" |
| |
| #include "QUICNewRenoCongestionController.h" |
| |
| #include "QUICStats.h" |
| #include "QUICGlobals.h" |
| #include "QUICDebugNames.h" |
| #include "QUICEvents.h" |
| #include "QUICHandshake.h" |
| #include "QUICConfig.h" |
| #include "QUICIntUtil.h" |
| |
| using namespace std::literals; |
| static constexpr std::string_view QUIC_DEBUG_TAG = "quic_net"sv; |
| |
| static constexpr uint16_t QUANTUM_TEST_ID = 3127; |
| static constexpr uint8_t QUANTUM_TEST_VALUE[1200] = {'Q'}; |
| |
| #define QUICConDebug(fmt, ...) Debug(QUIC_DEBUG_TAG.data(), "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) |
| |
| #define QUICConVDebug(fmt, ...) Debug("v_quic_net", "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) |
| #define QUICConVVVDebug(fmt, ...) Debug("vvv_quic_net", "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) |
| |
| #define QUICFCDebug(fmt, ...) Debug("quic_flow_ctrl", "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) |
| #define QUICFCVDebug(fmt, ...) Debug("v_quic_flow_ctrl", "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) |
| |
| #define QUICError(fmt, ...) \ |
| Debug("quic_net", "[%s] " fmt, this->cids().data(), ##__VA_ARGS__); \ |
| Error("quic_net [%s] " fmt, this->cids().data(), ##__VA_ARGS__) |
| |
| static constexpr uint32_t IPV4_HEADER_SIZE = 20; |
| static constexpr uint32_t IPV6_HEADER_SIZE = 40; |
| static constexpr uint32_t UDP_HEADER_SIZE = 8; |
| static constexpr uint32_t MAX_PACKET_OVERHEAD = 62; ///< Max long header len without length of token field of Initial packet |
| static constexpr uint32_t MINIMUM_INITIAL_PACKET_SIZE = 1200; |
| static constexpr ink_hrtime WRITE_READY_INTERVAL = HRTIME_MSECONDS(2); |
| static constexpr uint32_t PACKET_PER_EVENT = 256; |
| static constexpr uint32_t MAX_CONSECUTIVE_STREAMS = 8; ///< Interrupt sending STREAM frames to send ACK frame |
| // static constexpr uint32_t MIN_PKT_PAYLOAD_LEN = 3; ///< Minimum payload length for sampling for header protection |
| |
| static constexpr uint32_t STATE_CLOSING_MAX_SEND_PKT_NUM = 8; ///< Max number of sending packets which contain a closing frame. |
| static constexpr uint32_t STATE_CLOSING_MAX_RECV_PKT_WIND = 1 << STATE_CLOSING_MAX_SEND_PKT_NUM; |
| |
| ClassAllocator<QUICNetVConnection> quicNetVCAllocator("quicNetVCAllocator"); |
| |
| class QUICTPConfigQCP : public QUICTPConfig |
| { |
| public: |
| QUICTPConfigQCP(const QUICConfigParams *params, NetVConnectionContext_t ctx) : _params(params), _ctx(ctx) {} |
| |
| uint32_t |
| no_activity_timeout() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->no_activity_timeout_in(); |
| } else { |
| return this->_params->no_activity_timeout_out(); |
| } |
| } |
| |
| const IpEndpoint * |
| preferred_address_ipv4() const override |
| { |
| return this->_params->preferred_address_ipv4(); |
| } |
| |
| const IpEndpoint * |
| preferred_address_ipv6() const override |
| { |
| return this->_params->preferred_address_ipv6(); |
| } |
| |
| uint32_t |
| initial_max_data() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->initial_max_data_in(); |
| } else { |
| return this->_params->initial_max_data_out(); |
| } |
| } |
| |
| uint32_t |
| initial_max_stream_data_bidi_local() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->initial_max_stream_data_bidi_local_in(); |
| } else { |
| return this->_params->initial_max_stream_data_bidi_local_out(); |
| } |
| } |
| |
| uint32_t |
| initial_max_stream_data_bidi_remote() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->initial_max_stream_data_bidi_remote_in(); |
| } else { |
| return this->_params->initial_max_stream_data_bidi_remote_out(); |
| } |
| } |
| |
| uint32_t |
| initial_max_stream_data_uni() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->initial_max_stream_data_uni_in(); |
| } else { |
| return this->_params->initial_max_stream_data_uni_out(); |
| } |
| } |
| |
| uint64_t |
| initial_max_streams_bidi() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->initial_max_streams_bidi_in(); |
| } else { |
| return this->_params->initial_max_streams_bidi_out(); |
| } |
| } |
| |
| uint64_t |
| initial_max_streams_uni() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->initial_max_streams_uni_in(); |
| } else { |
| return this->_params->initial_max_streams_uni_out(); |
| } |
| } |
| |
| uint8_t |
| ack_delay_exponent() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->ack_delay_exponent_in(); |
| } else { |
| return this->_params->ack_delay_exponent_out(); |
| } |
| } |
| |
| uint8_t |
| max_ack_delay() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->max_ack_delay_in(); |
| } else { |
| return this->_params->max_ack_delay_out(); |
| } |
| } |
| |
| uint8_t |
| active_cid_limit() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->active_cid_limit_in(); |
| } else { |
| return this->_params->active_cid_limit_out(); |
| } |
| } |
| |
| bool |
| disable_active_migration() const override |
| { |
| if (this->_ctx == NET_VCONNECTION_IN) { |
| return this->_params->disable_active_migration(); |
| } else { |
| return false; |
| } |
| } |
| |
| const std::unordered_map<uint16_t, std::pair<const uint8_t *, uint16_t>> & |
| additional_tp() const override |
| { |
| return this->_additional_tp; |
| } |
| |
| void |
| add_tp(uint16_t id, const uint8_t *value, uint16_t length) |
| { |
| this->_additional_tp.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(value, length)); |
| } |
| |
| private: |
| const QUICConfigParams *_params; |
| NetVConnectionContext_t _ctx; |
| |
| std::unordered_map<uint16_t, std::pair<const uint8_t *, uint16_t>> _additional_tp; |
| }; |
| |
| QUICNetVConnection::QUICNetVConnection() : _packet_factory(this->_pp_key_info), _ph_protector(this->_pp_key_info) {} |
| |
| QUICNetVConnection::~QUICNetVConnection() |
| { |
| this->_unschedule_ack_manager_periodic(); |
| this->_unschedule_packet_write_ready(); |
| this->_unschedule_closing_timeout(); |
| this->_unschedule_closed_event(); |
| } |
| |
| // XXX This might be called on ET_UDP thread |
| // Initialize QUICNetVC for out going connection (NET_VCONNECTION_OUT) |
| void |
| QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *udp_con, |
| QUICPacketHandler *packet_handler, QUICResetTokenTable *rtable) |
| { |
| this->_initial_version = version; |
| this->_udp_con = udp_con; |
| this->_packet_handler = packet_handler; |
| this->_peer_quic_connection_id = peer_cid; |
| this->_original_quic_connection_id = original_cid; |
| this->_rtable = rtable; |
| this->_quic_connection_id.randomize(); |
| this->_initial_source_connection_id = this->_quic_connection_id; |
| |
| this->_update_cids(); |
| |
| if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { |
| QUICConDebug("dcid=%s scid=%s", this->_peer_quic_connection_id.hex().c_str(), this->_quic_connection_id.hex().c_str()); |
| } |
| |
| this->_init_submodules(); |
| } |
| |
| // Initialize QUICNetVC for in coming connection (NET_VCONNECTION_IN) |
| void |
| QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, |
| QUICConnectionId retry_cid, UDPConnection *udp_con, QUICPacketHandler *packet_handler, |
| QUICResetTokenTable *rtable, QUICConnectionTable *ctable) |
| { |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent); |
| this->_initial_version = version; |
| this->_udp_con = udp_con; |
| this->_packet_handler = packet_handler; |
| this->_peer_quic_connection_id = peer_cid; |
| this->_original_quic_connection_id = original_cid; |
| this->_first_quic_connection_id = first_cid; |
| this->_retry_source_connection_id = retry_cid; |
| this->_quic_connection_id.randomize(); |
| this->_initial_source_connection_id = this->_quic_connection_id; |
| |
| if (ctable) { |
| this->_ctable = ctable; |
| this->_ctable->insert(this->_quic_connection_id, this); |
| this->_ctable->insert(this->_original_quic_connection_id, this); |
| } |
| this->_rtable = rtable; |
| |
| this->_update_cids(); |
| |
| if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { |
| QUICConDebug("dcid=%s scid=%s", this->_peer_quic_connection_id.hex().c_str(), this->_quic_connection_id.hex().c_str()); |
| } |
| |
| this->_init_submodules(); |
| } |
| |
| void |
| QUICNetVConnection::_init_submodules() |
| { |
| this->_pinger = new QUICPinger(); |
| this->_padder = new QUICPadder(this->netvc_context); |
| this->_path_validator = new QUICPathValidator(*this, [this](bool succeeded) { |
| if (succeeded) { |
| this->_alt_con_manager->drop_cid(this->_peer_old_quic_connection_id); |
| // FIXME This is a kind of workaround for connection migration. |
| // This PING make peer to send an ACK frame so that ATS can detect packet loss. |
| // It would be better if QUICLossDetector could detect the loss in another way. |
| this->ping(); |
| } |
| }); |
| this->_path_manager = new QUICPathManagerImpl(*this, *this->_path_validator); |
| this->_context = std::make_unique<QUICContext>(&this->_rtt_measure, this, &this->_pp_key_info, this->_path_manager); |
| this->_congestion_controller = new QUICNewRenoCongestionController(*_context); |
| this->_rtt_measure.init(this->_context->ld_config()); |
| this->_loss_detector = |
| new QUICLossDetector(*_context, this->_congestion_controller, &this->_rtt_measure, this->_pinger, this->_padder); |
| } |
| |
| bool |
| QUICNetVConnection::shouldDestroy() |
| { |
| return this->refcount() == 0; |
| } |
| |
| VIO * |
| QUICNetVConnection::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf) |
| { |
| ink_assert(false); |
| return nullptr; |
| } |
| |
| VIO * |
| QUICNetVConnection::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner) |
| { |
| ink_assert(false); |
| return nullptr; |
| } |
| |
| int |
| QUICNetVConnection::acceptEvent(int event, Event *e) |
| { |
| EThread *t = (e == nullptr) ? this_ethread() : e->ethread; |
| NetHandler *h = get_NetHandler(t); |
| |
| MUTEX_TRY_LOCK(lock, h->mutex, t); |
| if (!lock.is_locked()) { |
| if (event == EVENT_NONE) { |
| t->schedule_in(this, HRTIME_MSECONDS(net_retry_delay)); |
| return EVENT_DONE; |
| } else { |
| e->schedule_in(HRTIME_MSECONDS(net_retry_delay)); |
| return EVENT_CONT; |
| } |
| } |
| |
| // this->thread is already assigned by QUICPacketHandlerIn::_recv_packet |
| ink_assert(this->thread == this_ethread()); |
| |
| // Send this NetVC to NetHandler and start to polling read & write event. |
| if (h->startIO(this) < 0) { |
| free(t); |
| return EVENT_DONE; |
| } |
| |
| // FIXME: complete do_io_xxxx instead |
| this->read.enabled = 1; |
| |
| // Handshake callback handler. |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_pre_handshake); |
| |
| // Send this netvc to InactivityCop. |
| nh->startCop(this); |
| |
| if (inactivity_timeout_in) { |
| set_inactivity_timeout(inactivity_timeout_in); |
| } else { |
| set_inactivity_timeout(0); |
| } |
| |
| if (active_timeout_in) { |
| set_active_timeout(active_timeout_in); |
| } |
| |
| this->start(); |
| |
| action_.continuation->handleEvent(NET_EVENT_ACCEPT, this); |
| this->_schedule_packet_write_ready(); |
| |
| return EVENT_DONE; |
| } |
| |
| // XXX This might be called on ET_UDP thread |
| void |
| QUICNetVConnection::start() |
| { |
| ink_release_assert(this->thread != nullptr); |
| this->_five_tuple.update(this->local_addr, this->remote_addr, SOCK_DGRAM); |
| QUICPath trusted_path = {{}, {}}; |
| // Version 0x00000001 uses stream 0 for cryptographic handshake with TLS 1.3, but newer version may not |
| if (this->direction() == NET_VCONNECTION_IN) { |
| QUICCertConfig::scoped_config server_cert; |
| |
| this->_pp_key_info.set_context(QUICPacketProtectionKeyInfo::Context::SERVER); |
| this->_ack_frame_manager.set_ack_delay_exponent(this->_quic_config->ack_delay_exponent_in()); |
| this->_reset_token = QUICStatelessResetToken(this->_quic_connection_id, this->_quic_config->instance_id()); |
| this->_hs_protocol = this->_setup_handshake_protocol(server_cert->ssl_default); |
| this->_handshake_handler = new QUICHandshake(this->_initial_version, this, this->_hs_protocol, this->_reset_token, |
| this->_quic_config->stateless_retry()); |
| this->_ack_frame_manager.set_max_ack_delay(this->_quic_config->max_ack_delay_in()); |
| this->_schedule_ack_manager_periodic(this->_quic_config->max_ack_delay_in()); |
| } else { |
| trusted_path = {this->local_addr, this->remote_addr}; |
| QUICTPConfigQCP tp_config(this->_quic_config, NET_VCONNECTION_OUT); |
| if (this->_quic_config->quantum_readiness_test_enabled_out()) { |
| tp_config.add_tp(QUANTUM_TEST_ID, QUANTUM_TEST_VALUE, sizeof(QUANTUM_TEST_VALUE)); |
| } |
| this->_pp_key_info.set_context(QUICPacketProtectionKeyInfo::Context::CLIENT); |
| this->_ack_frame_manager.set_ack_delay_exponent(this->_quic_config->ack_delay_exponent_out()); |
| this->_hs_protocol = this->_setup_handshake_protocol(this->_quic_config->client_ssl_ctx()); |
| this->_handshake_handler = new QUICHandshake(this->_initial_version, this, this->_hs_protocol); |
| this->_record_tls_handshake_begin_time(); |
| this->_handshake_handler->start(tp_config, &this->_packet_factory, this->_quic_config->vn_exercise_enabled()); |
| this->_handshake_handler->do_handshake(); |
| this->_ack_frame_manager.set_max_ack_delay(this->_quic_config->max_ack_delay_out()); |
| this->_schedule_ack_manager_periodic(this->_quic_config->max_ack_delay_out()); |
| } |
| this->_path_manager->set_trusted_path(trusted_path); |
| |
| this->_application_map = new QUICApplicationMap(); |
| |
| this->_frame_dispatcher = new QUICFrameDispatcher(this); |
| |
| // Create frame handlers |
| this->_frame_dispatcher->add_handler(this->_loss_detector); |
| |
| this->_remote_flow_controller = new QUICRemoteConnectionFlowController(UINT64_MAX); |
| this->_local_flow_controller = new QUICLocalConnectionFlowController(&this->_rtt_measure, UINT64_MAX); |
| this->_stream_manager = new QUICStreamManager(this->_context.get(), this->_application_map); |
| this->_token_creator = new QUICTokenCreator(this->_context.get()); |
| |
| static constexpr int QUIC_STREAM_MANAGER_WEIGHT = QUICFrameGeneratorWeight::AFTER_DATA - 1; |
| static constexpr int QUIC_PINGER_WEIGHT = QUICFrameGeneratorWeight::LATE + 1; |
| static constexpr int QUIC_PADDER_WEIGHT = QUICFrameGeneratorWeight::LATE + 2; |
| |
| // Register frame generators |
| this->_frame_generators.add_generator(*this->_handshake_handler, QUICFrameGeneratorWeight::EARLY); // CRYPTO |
| this->_frame_generators.add_generator(*this->_path_validator, QUICFrameGeneratorWeight::EARLY); // PATH_CHALLENGE, PATH_RESPOSNE |
| this->_frame_generators.add_generator(*this->_local_flow_controller, QUICFrameGeneratorWeight::BEFORE_DATA); // MAX_DATA |
| this->_frame_generators.add_generator(*this->_remote_flow_controller, QUICFrameGeneratorWeight::BEFORE_DATA); // DATA_BLOCKED |
| this->_frame_generators.add_generator(*this->_token_creator, QUICFrameGeneratorWeight::BEFORE_DATA); // NEW_TOKEN |
| this->_frame_generators.add_generator(*this->_stream_manager, |
| QUIC_STREAM_MANAGER_WEIGHT); // STREAM, MAX_STREAM_DATA, STREAM_DATA_BLOCKED |
| this->_frame_generators.add_generator(this->_ack_frame_manager, QUICFrameGeneratorWeight::BEFORE_DATA); // ACK |
| |
| this->_frame_generators.add_generator(*this->_pinger, QUIC_PINGER_WEIGHT); // PING |
| // Warning: padder should be tail of the frame generators |
| this->_frame_generators.add_generator(*this->_padder, QUIC_PADDER_WEIGHT); // PADDING |
| |
| // Register frame handlers |
| this->_frame_dispatcher->add_handler(this); |
| this->_frame_dispatcher->add_handler(this->_stream_manager); |
| this->_frame_dispatcher->add_handler(this->_path_validator); |
| this->_frame_dispatcher->add_handler(this->_handshake_handler); |
| |
| // register qlog |
| if (this->_context->config()->qlog_dir() != nullptr) { |
| this->_qlog = std::make_unique<QLog::QLogListener>(*this->_context, this->_original_quic_connection_id.hex()); |
| this->_qlog->last_trace().set_vantage_point( |
| {"ats", QLog::Trace::VantagePointType::server, QLog::Trace::VantagePointType::server}); |
| this->_context->regist_callback(this->_qlog); |
| } |
| } |
| |
| void |
| QUICNetVConnection::free(EThread *t) |
| { |
| QUICConDebug("Free connection"); |
| |
| /* TODO: Uncmment these blocks after refactoring read / write process |
| this->_udp_con = nullptr; |
| this->_packet_handler = nullptr; |
| |
| _unschedule_packet_write_ready(); |
| |
| delete this->_handshake_handler; |
| delete this->_application_map; |
| delete this->_hs_protocol; |
| delete this->_loss_detector; |
| delete this->_frame_dispatcher; |
| delete this->_stream_manager; |
| delete this->_congestion_controller; |
| if (this->_alt_con_manager) { |
| delete this->_alt_con_manager; |
| } |
| |
| super::clear(); |
| */ |
| this->_context->trigger(QUICContext::CallbackEvent::CONNECTION_CLOSE); |
| ALPNSupport::clear(); |
| TLSSessionResumptionSupport::clear(); |
| TLSBasicSupport::clear(); |
| this->_packet_handler->close_connection(this); |
| } |
| |
| void |
| QUICNetVConnection::free() |
| { |
| this->free(this_ethread()); |
| } |
| |
| // called by ET_UDP |
| void |
| QUICNetVConnection::remove_connection_ids() |
| { |
| if (this->_ctable) { |
| this->_ctable->erase(this->_original_quic_connection_id, this); |
| this->_ctable->erase(this->_quic_connection_id, this); |
| } |
| |
| if (this->_alt_con_manager) { |
| this->_alt_con_manager->invalidate_alt_connections(); |
| } |
| } |
| |
| // called by ET_UDP |
| void |
| QUICNetVConnection::destroy(EThread *t) |
| { |
| QUICConDebug("Destroy connection"); |
| /* TODO: Uncomment these blocks after refactoring read / write process |
| if (from_accept_thread) { |
| quicNetVCAllocator.free(this); |
| } else { |
| THREAD_FREE(this, quicNetVCAllocator, t); |
| } |
| */ |
| } |
| |
| void |
| QUICNetVConnection::set_local_addr() |
| { |
| int local_sa_size = sizeof(local_addr); |
| ATS_UNUSED_RETURN(safe_getsockname(this->_udp_con->getFd(), &local_addr.sa, &local_sa_size)); |
| } |
| |
| void |
| QUICNetVConnection::reenable(VIO *vio) |
| { |
| return; |
| } |
| |
| int |
| QUICNetVConnection::connectUp(EThread *t, int fd) |
| { |
| int res = 0; |
| NetHandler *nh = get_NetHandler(t); |
| this->thread = this_ethread(); |
| ink_assert(nh->mutex->thread_holding == this->thread); |
| |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_pre_handshake); |
| |
| if ((res = nh->startIO(this)) < 0) { |
| // FIXME: startIO only return 0 now! what should we do if it failed ? |
| } |
| |
| nh->startCop(this); |
| |
| // FIXME: complete do_io_xxxx instead |
| this->read.enabled = 1; |
| |
| this->set_local_addr(); |
| this->remote_addr = con.addr; |
| |
| this->start(); |
| |
| // start QUIC handshake |
| this->_schedule_packet_write_ready(); |
| |
| return CONNECT_SUCCESS; |
| } |
| |
| QUICConnectionId |
| QUICNetVConnection::peer_connection_id() const |
| { |
| return this->_peer_quic_connection_id; |
| } |
| |
| QUICConnectionId |
| QUICNetVConnection::original_connection_id() const |
| { |
| return this->_original_quic_connection_id; |
| } |
| |
| QUICConnectionId |
| QUICNetVConnection::first_connection_id() const |
| { |
| return this->_first_quic_connection_id; |
| } |
| |
| QUICConnectionId |
| QUICNetVConnection::retry_source_connection_id() const |
| { |
| return this->_retry_source_connection_id; |
| } |
| |
| QUICConnectionId |
| QUICNetVConnection::initial_source_connection_id() const |
| { |
| return this->_initial_source_connection_id; |
| } |
| |
| QUICConnectionId |
| QUICNetVConnection::connection_id() const |
| { |
| return this->_quic_connection_id; |
| } |
| |
| /* |
| Return combination of dst connection id and src connection id for debug log |
| e.g. "aaaaaaaa-bbbbbbbb" |
| - "aaaaaaaa" : high 32 bit of dst connection id |
| - "bbbbbbbb" : high 32 bit of src connection id |
| */ |
| std::string_view |
| QUICNetVConnection::cids() const |
| { |
| return this->_cids; |
| } |
| |
| const QUICFiveTuple |
| QUICNetVConnection::five_tuple() const |
| { |
| return this->_five_tuple; |
| } |
| |
| uint32_t |
| QUICNetVConnection::pmtu() const |
| { |
| return this->_pmtu; |
| } |
| |
| NetVConnectionContext_t |
| QUICNetVConnection::direction() const |
| { |
| return this->netvc_context; |
| } |
| |
| uint32_t |
| QUICNetVConnection::_minimum_quic_packet_size() |
| { |
| if (netvc_context == NET_VCONNECTION_OUT) { |
| // FIXME Only the first packet need to be 1200 bytes at least |
| return MINIMUM_INITIAL_PACKET_SIZE; |
| } else { |
| // FIXME This size should be configurable and should have some randomness |
| // This is just for providing protection against packet analysis for protected packets |
| return 32 + (this->_rnd() & 0x3f); // 32 to 96 |
| } |
| } |
| |
| uint32_t |
| QUICNetVConnection::_maximum_quic_packet_size() const |
| { |
| if (this->options.ip_family == PF_INET6) { |
| return this->_pmtu - UDP_HEADER_SIZE - IPV6_HEADER_SIZE; |
| } else { |
| return this->_pmtu - UDP_HEADER_SIZE - IPV4_HEADER_SIZE; |
| } |
| } |
| |
| uint64_t |
| QUICNetVConnection::_maximum_stream_frame_data_size() |
| { |
| return this->_maximum_quic_packet_size() - MAX_STREAM_FRAME_OVERHEAD - MAX_PACKET_OVERHEAD; |
| } |
| |
| QUICStreamManager * |
| QUICNetVConnection::stream_manager() |
| { |
| return this->_stream_manager; |
| } |
| |
| void |
| QUICNetVConnection::handle_received_packet(UDPPacket *packet) |
| { |
| this->_packet_recv_queue.enqueue(packet); |
| this->_loss_detector->on_datagram_received(); |
| } |
| |
| void |
| QUICNetVConnection::ping() |
| { |
| this->_pinger->request(QUICEncryptionLevel::ONE_RTT); |
| } |
| |
| void |
| QUICNetVConnection::close_quic_connection(QUICConnectionErrorUPtr error) |
| { |
| if (this->handler == reinterpret_cast<ContinuationHandler>(&QUICNetVConnection::state_connection_closed) || |
| this->handler == reinterpret_cast<ContinuationHandler>(&QUICNetVConnection::state_connection_closing)) { |
| // do nothing |
| } else { |
| this->_switch_to_closing_state(std::move(error)); |
| } |
| } |
| |
| void |
| QUICNetVConnection::reset_quic_connection() |
| { |
| this->_switch_to_close_state(); |
| |
| QUICStatelessResetToken token(this->connection_id(), this->_quic_config->instance_id()); |
| auto packet = QUICPacketFactory::create_stateless_reset_packet(token, 128); |
| if (packet) { |
| Ptr<IOBufferBlock> udp_payload(new_IOBufferBlock()); |
| udp_payload->alloc(iobuffer_size_to_index(128, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(udp_payload->end()); |
| size_t len = 0; |
| packet->store(buf, &len); |
| udp_payload->fill(len); |
| this->_packet_handler->send_packet(this, udp_payload); |
| } |
| } |
| |
| std::vector<QUICFrameType> |
| QUICNetVConnection::interests() |
| { |
| return {QUICFrameType::CONNECTION_CLOSE, QUICFrameType::DATA_BLOCKED, QUICFrameType::MAX_DATA}; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::handle_frame(QUICEncryptionLevel level, const QUICFrame &frame) |
| { |
| QUICConnectionErrorUPtr error = nullptr; |
| |
| switch (frame.type()) { |
| case QUICFrameType::MAX_DATA: |
| this->_remote_flow_controller->forward_limit(static_cast<const QUICMaxDataFrame &>(frame).maximum_data()); |
| QUICFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller->current_offset(), |
| this->_remote_flow_controller->current_limit()); |
| this->_schedule_packet_write_ready(); |
| break; |
| case QUICFrameType::DATA_BLOCKED: |
| // DATA_BLOCKED frame is for debugging. Nothing to do here. |
| break; |
| case QUICFrameType::CONNECTION_CLOSE: |
| if (this->handler == reinterpret_cast<NetVConnHandler>(&QUICNetVConnection::state_connection_closed) || |
| this->handler == reinterpret_cast<NetVConnHandler>(&QUICNetVConnection::state_connection_draining)) { |
| return error; |
| } |
| |
| // 7.9.1. Closing and Draining Connection States |
| // An endpoint MAY transition from the closing period to the draining period if it can confirm that its peer is also closing or |
| // draining. Receiving a closing frame is sufficient confirmation, as is receiving a stateless reset. |
| { |
| uint16_t error_code = static_cast<const QUICConnectionCloseFrame &>(frame).error_code(); |
| this->_switch_to_draining_state( |
| QUICConnectionErrorUPtr(std::make_unique<QUICConnectionError>(static_cast<QUICTransErrorCode>(error_code)))); |
| } |
| break; |
| default: |
| QUICConDebug("Unexpected frame type: %02x", static_cast<unsigned int>(frame.type())); |
| ink_assert(false); |
| break; |
| } |
| |
| return error; |
| } |
| |
| // XXX Setup QUICNetVConnection on regular EThread. |
| // QUICNetVConnection::init() might be called on ET_UDP EThread. |
| int |
| QUICNetVConnection::state_pre_handshake(int event, Event *data) |
| { |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| |
| // this->thread should be assigned on any direction |
| ink_assert(this->thread == this_ethread()); |
| |
| if (!this->nh) { |
| this->nh = get_NetHandler(this_ethread()); |
| } |
| |
| // FIXME: Should be accept_no_activity_timeout? |
| if (this->get_context() == NET_VCONNECTION_IN) { |
| this->set_inactivity_timeout(HRTIME_MSECONDS(this->_quic_config->no_activity_timeout_in())); |
| } else { |
| this->set_inactivity_timeout(HRTIME_MSECONDS(this->_quic_config->no_activity_timeout_out())); |
| } |
| |
| this->add_to_active_queue(); |
| |
| this->_switch_to_handshake_state(); |
| return this->handleEvent(event, data); |
| } |
| |
| // TODO: Timeout by active_timeout |
| int |
| QUICNetVConnection::state_handshake(int event, Event *data) |
| { |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| |
| if (this->_handshake_handler && this->_handshake_handler->is_completed()) { |
| this->_switch_to_established_state(); |
| return this->handleEvent(event, data); |
| } |
| |
| QUICConnectionErrorUPtr error = nullptr; |
| |
| switch (event) { |
| case QUIC_EVENT_PACKET_READ_READY: { |
| QUICPacketCreationResult result; |
| net_activity(this, this_ethread()); |
| do { |
| uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; |
| QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); |
| if (result == QUICPacketCreationResult::NOT_READY) { |
| error = nullptr; |
| } else if (result == QUICPacketCreationResult::FAILED) { |
| // Don't make this error, and discard the packet. |
| // Because: |
| // - Attacker can terminate connections |
| // - It could be just an error on lower layer |
| error = nullptr; |
| } else if (result == QUICPacketCreationResult::SUCCESS || result == QUICPacketCreationResult::UNSUPPORTED) { |
| error = this->_state_handshake_process_packet(*packet); |
| } |
| |
| // if we complete handshake, switch to establish state |
| if (this->_handshake_handler && this->_handshake_handler->is_completed()) { |
| this->_switch_to_established_state(); |
| return this->handleEvent(event, data); |
| } |
| |
| } while (error == nullptr && (result == QUICPacketCreationResult::SUCCESS || result == QUICPacketCreationResult::IGNORED)); |
| break; |
| } |
| case QUIC_EVENT_STATELESS_RESET: |
| this->_switch_to_draining_state(std::make_unique<QUICConnectionError>(QUICTransErrorCode::NO_ERROR, "Stateless Reset")); |
| break; |
| case QUIC_EVENT_ACK_PERIODIC: |
| this->_handle_periodic_ack_event(); |
| break; |
| case QUIC_EVENT_PACKET_WRITE_READY: |
| this->_close_packet_write_ready(data); |
| // TODO: support RETRY packet |
| error = this->_state_common_send_packet(); |
| // Reschedule WRITE_READY |
| this->_schedule_packet_write_ready(true); |
| break; |
| case VC_EVENT_INACTIVITY_TIMEOUT: |
| // Start Immediate Close because of Idle Timeout |
| this->_handle_idle_timeout(); |
| break; |
| case VC_EVENT_ACTIVE_TIMEOUT: |
| // Start Immediate Close |
| this->_handle_active_timeout(); |
| break; |
| default: |
| QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); |
| } |
| |
| if (error != nullptr) { |
| this->_handle_error(std::move(error)); |
| } |
| |
| return EVENT_CONT; |
| } |
| |
| int |
| QUICNetVConnection::state_connection_established(int event, Event *data) |
| { |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| QUICConnectionErrorUPtr error = nullptr; |
| switch (event) { |
| case QUIC_EVENT_PACKET_READ_READY: |
| error = this->_state_connection_established_receive_packet(); |
| break; |
| case QUIC_EVENT_ACK_PERIODIC: |
| this->_handle_periodic_ack_event(); |
| break; |
| case QUIC_EVENT_PACKET_WRITE_READY: |
| this->_close_packet_write_ready(data); |
| error = this->_state_common_send_packet(); |
| // Reschedule WRITE_READY |
| this->_schedule_packet_write_ready(true); |
| break; |
| case QUIC_EVENT_STATELESS_RESET: |
| this->_switch_to_draining_state(std::make_unique<QUICConnectionError>(QUICTransErrorCode::NO_ERROR, "Stateless Reset")); |
| break; |
| case VC_EVENT_INACTIVITY_TIMEOUT: |
| // Start Immediate Close because of Idle Timeout |
| this->_handle_idle_timeout(); |
| break; |
| case VC_EVENT_ACTIVE_TIMEOUT: |
| // Start Immediate Close |
| this->_handle_active_timeout(); |
| break; |
| default: |
| QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); |
| } |
| |
| if (error != nullptr) { |
| QUICConDebug("QUICError: cls=%u, code=0x%" PRIx16, static_cast<unsigned int>(error->cls), error->code); |
| this->_handle_error(std::move(error)); |
| } |
| |
| return EVENT_CONT; |
| } |
| |
| int |
| QUICNetVConnection::state_connection_closing(int event, Event *data) |
| { |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| |
| QUICConnectionErrorUPtr error = nullptr; |
| switch (event) { |
| case QUIC_EVENT_PACKET_READ_READY: |
| error = this->_state_closing_receive_packet(); |
| break; |
| case QUIC_EVENT_PACKET_WRITE_READY: |
| this->_close_packet_write_ready(data); |
| this->_state_closing_send_packet(); |
| break; |
| case QUIC_EVENT_CLOSING_TIMEOUT: |
| this->_close_closing_timeout(data); |
| this->_switch_to_close_state(); |
| break; |
| case QUIC_EVENT_STATELESS_RESET: |
| break; |
| case VC_EVENT_ACTIVE_TIMEOUT: |
| // Do nothing because closing is in progress |
| break; |
| case QUIC_EVENT_ACK_PERIODIC: |
| default: |
| QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); |
| ink_assert(false); |
| } |
| |
| return EVENT_DONE; |
| } |
| |
| int |
| QUICNetVConnection::state_connection_draining(int event, Event *data) |
| { |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| |
| QUICConnectionErrorUPtr error = nullptr; |
| switch (event) { |
| case QUIC_EVENT_PACKET_READ_READY: |
| error = this->_state_draining_receive_packet(); |
| break; |
| case QUIC_EVENT_PACKET_WRITE_READY: |
| // Do not send any packets in this state. |
| // This should be the only difference between this and closing_state. |
| this->_close_packet_write_ready(data); |
| break; |
| case QUIC_EVENT_CLOSING_TIMEOUT: |
| this->_close_closing_timeout(data); |
| this->_switch_to_close_state(); |
| break; |
| case QUIC_EVENT_STATELESS_RESET: |
| break; |
| case VC_EVENT_ACTIVE_TIMEOUT: |
| // Do nothing because closing is in progress |
| break; |
| case QUIC_EVENT_ACK_PERIODIC: |
| default: |
| QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); |
| ink_assert(false); |
| } |
| |
| return EVENT_DONE; |
| } |
| |
| int |
| QUICNetVConnection::state_connection_closed(int event, Event *data) |
| { |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| switch (event) { |
| case QUIC_EVENT_SHUTDOWN: { |
| this->_unschedule_ack_manager_periodic(); |
| this->_unschedule_packet_write_ready(); |
| this->_unschedule_closing_timeout(); |
| this->_close_closed_event(data); |
| this->next_inactivity_timeout_at = 0; |
| this->next_activity_timeout_at = 0; |
| |
| this->inactivity_timeout_in = 0; |
| this->active_timeout_in = 0; |
| |
| // TODO: Drop record from Connection-ID - QUICNetVConnection table in QUICPacketHandler |
| // Shutdown loss detector |
| SCOPED_MUTEX_LOCK(lock2, this->_loss_detector->mutex, this_ethread()); |
| this->_loss_detector->handleEvent(QUIC_EVENT_LD_SHUTDOWN, nullptr); |
| |
| // FIXME I'm not sure whether we can block here, but it's needed to not crash. |
| SCOPED_MUTEX_LOCK(lock, this->nh->mutex, this_ethread()); |
| if (this->nh) { |
| this->nh->free_netevent(this); |
| } else { |
| this->free(this->mutex->thread_holding); |
| } |
| break; |
| } |
| case QUIC_EVENT_PACKET_WRITE_READY: { |
| this->_close_packet_write_ready(data); |
| break; |
| } |
| default: |
| QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); |
| } |
| |
| return EVENT_DONE; |
| } |
| |
| UDPConnection * |
| QUICNetVConnection::get_udp_con() |
| { |
| return this->_udp_con; |
| } |
| |
| void |
| QUICNetVConnection::net_read_io(NetHandler *nh, EThread *lthread) |
| { |
| SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); |
| this->handleEvent(QUIC_EVENT_PACKET_READ_READY, nullptr); |
| |
| return; |
| } |
| |
| int64_t |
| QUICNetVConnection::load_buffer_and_write(int64_t towrite, MIOBufferAccessor &buf, int64_t &total_written, int &needs) |
| { |
| ink_assert(false); |
| |
| return 0; |
| } |
| |
| int |
| QUICNetVConnection::populate_protocol(std::string_view *results, int n) const |
| { |
| int retval = 0; |
| if (n > retval) { |
| results[retval++] = IP_PROTO_TAG_QUIC; |
| if (n > retval) { |
| results[retval++] = IP_PROTO_TAG_TLS_1_3; |
| if (n > retval) { |
| retval += super::populate_protocol(results + retval, n - retval); |
| } |
| } |
| } |
| return retval; |
| } |
| |
| const char * |
| QUICNetVConnection::protocol_contains(std::string_view prefix) const |
| { |
| const char *retval = nullptr; |
| if (prefix.size() <= IP_PROTO_TAG_QUIC.size() && strncmp(IP_PROTO_TAG_QUIC.data(), prefix.data(), prefix.size()) == 0) { |
| retval = IP_PROTO_TAG_QUIC.data(); |
| } else if (prefix.size() <= IP_PROTO_TAG_TLS_1_3.size() && |
| strncmp(IP_PROTO_TAG_TLS_1_3.data(), prefix.data(), prefix.size()) == 0) { |
| retval = IP_PROTO_TAG_TLS_1_3.data(); |
| } else { |
| retval = super::protocol_contains(prefix); |
| } |
| return retval; |
| } |
| |
| bool |
| QUICNetVConnection::is_closed() const |
| { |
| return this->handler == reinterpret_cast<NetVConnHandler>(&QUICNetVConnection::state_connection_closed); |
| } |
| |
| bool |
| QUICNetVConnection::is_at_anti_amplification_limit() const |
| { |
| return !this->_verified_state.is_verified() || this->_verified_state.windows() == 0; |
| } |
| |
| bool |
| QUICNetVConnection::is_address_validation_completed() const |
| { |
| return this->_verified_state.is_verified(); |
| } |
| |
| bool |
| QUICNetVConnection::is_handshake_completed() const |
| { |
| return this->_handshake_completed; |
| } |
| |
| bool |
| QUICNetVConnection::has_keys_for(QUICPacketNumberSpace space) const |
| { |
| switch (space) { |
| case QUICPacketNumberSpace::INITIAL: |
| return this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL) && |
| this->_pp_key_info.is_encryption_key_available(QUICKeyPhase::INITIAL); |
| case QUICPacketNumberSpace::HANDSHAKE: |
| return this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::HANDSHAKE) && |
| this->_pp_key_info.is_encryption_key_available(QUICKeyPhase::HANDSHAKE); |
| case QUICPacketNumberSpace::APPLICATION_DATA: |
| return (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::PHASE_0) && |
| this->_pp_key_info.is_encryption_key_available(QUICKeyPhase::PHASE_0)) || |
| (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::PHASE_1) && |
| this->_pp_key_info.is_encryption_key_available(QUICKeyPhase::PHASE_1)); |
| default: |
| return false; |
| } |
| } |
| |
| QUICPacketNumber |
| QUICNetVConnection::_largest_acked_packet_number(QUICEncryptionLevel level) const |
| { |
| auto index = QUICTypeUtil::pn_space(level); |
| |
| return this->_loss_detector->largest_acked_packet_number(index); |
| } |
| |
| QUICVersion |
| QUICNetVConnection::negotiated_version() const |
| { |
| return this->_handshake_handler->negotiated_version(); |
| } |
| |
| std::string_view |
| QUICNetVConnection::negotiated_application_name() const |
| { |
| const uint8_t *name; |
| unsigned int name_len = 0; |
| |
| this->_hs_protocol->negotiated_application_name(&name, &name_len); |
| |
| return std::string_view(reinterpret_cast<const char *>(name), name_len); |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_handshake_process_packet(const QUICPacket &packet) |
| { |
| QUICConnectionErrorUPtr error = nullptr; |
| switch (packet.type()) { |
| case QUICPacketType::VERSION_NEGOTIATION: |
| error = this->_state_handshake_process_version_negotiation_packet(static_cast<const QUICVersionNegotiationPacketR &>(packet)); |
| break; |
| case QUICPacketType::INITIAL: |
| error = this->_state_handshake_process_initial_packet(static_cast<const QUICInitialPacketR &>(packet)); |
| break; |
| case QUICPacketType::RETRY: |
| error = this->_state_handshake_process_retry_packet(static_cast<const QUICRetryPacketR &>(packet)); |
| break; |
| case QUICPacketType::HANDSHAKE: |
| error = this->_state_handshake_process_handshake_packet(static_cast<const QUICHandshakePacketR &>(packet)); |
| if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL) && this->netvc_context == NET_VCONNECTION_IN) { |
| this->_pp_key_info.drop_keys(QUICKeyPhase::INITIAL); |
| this->_loss_detector->on_packet_number_space_discarded(QUICPacketNumberSpace::INITIAL); |
| this->_minimum_encryption_level = QUICEncryptionLevel::HANDSHAKE; |
| } |
| break; |
| case QUICPacketType::ZERO_RTT_PROTECTED: |
| error = this->_state_handshake_process_zero_rtt_protected_packet(static_cast<const QUICZeroRttPacketR &>(packet)); |
| break; |
| case QUICPacketType::PROTECTED: |
| QUICConDebug("Ignore %s(%" PRIu8 ") packet", QUICDebugNames::packet_type(packet.type()), static_cast<uint8_t>(packet.type())); |
| break; |
| default: |
| error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::INTERNAL_ERROR); |
| break; |
| } |
| return error; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_handshake_process_version_negotiation_packet(const QUICVersionNegotiationPacketR &packet) |
| { |
| QUICConnectionErrorUPtr error = nullptr; |
| |
| if (packet.destination_cid() != this->connection_id()) { |
| QUICConDebug("Ignore Version Negotiation packet"); |
| return error; |
| } |
| |
| if (this->_handshake_handler->is_version_negotiated()) { |
| QUICConDebug("ignore VN - already negotiated"); |
| } else { |
| error = this->_handshake_handler->negotiate_version(packet, &this->_packet_factory); |
| |
| // discard all transport state except packet number |
| this->_loss_detector->reset(); |
| |
| this->_congestion_controller->reset(); |
| |
| // start handshake over |
| this->_handshake_handler->reset(); |
| this->_handshake_handler->do_handshake(); |
| this->_schedule_packet_write_ready(); |
| } |
| |
| return error; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_handshake_process_initial_packet(const QUICInitialPacketR &packet) |
| { |
| // QUIC packet could be smaller than MINIMUM_INITIAL_PACKET_SIZE when coalescing packets |
| // if (packet->size() < MINIMUM_INITIAL_PACKET_SIZE) { |
| // QUICConDebug("Packet size is smaller than the minimum initial packet size"); |
| // // Ignore the packet |
| // return QUICErrorUPtr(new QUICNoError()); |
| // } |
| |
| QUICConnectionErrorUPtr error = nullptr; |
| |
| // Start handshake |
| if (this->netvc_context == NET_VCONNECTION_IN) { |
| this->local_addr.sa = packet.to().sa; |
| this->remote_addr.sa = packet.from().sa; |
| this->_five_tuple.update(this->local_addr, this->remote_addr, SOCK_DGRAM); |
| |
| if (!this->_alt_con_manager) { |
| this->_alt_con_manager = |
| new QUICAltConnectionManager(this, *this->_ctable, *this->_rtable, this->_peer_quic_connection_id, |
| this->_quic_config->instance_id(), this->_quic_config->active_cid_limit_in(), |
| this->_quic_config->preferred_address_ipv4(), this->_quic_config->preferred_address_ipv6()); |
| this->_frame_generators.add_generator(*this->_alt_con_manager, QUICFrameGeneratorWeight::EARLY); |
| this->_frame_dispatcher->add_handler(this->_alt_con_manager); |
| } |
| QUICTPConfigQCP tp_config(this->_quic_config, NET_VCONNECTION_IN); |
| if (this->_quic_config->quantum_readiness_test_enabled_in()) { |
| tp_config.add_tp(QUANTUM_TEST_ID, QUANTUM_TEST_VALUE, sizeof(QUANTUM_TEST_VALUE)); |
| } |
| this->_record_tls_handshake_begin_time(); |
| error = this->_handshake_handler->start(tp_config, packet, &this->_packet_factory, this->_alt_con_manager->preferred_address()); |
| |
| // If version negotiation was failed and VERSION NEGOTIATION packet was sent, nothing to do. |
| if (this->_handshake_handler->is_version_negotiated()) { |
| error = this->_recv_and_ack(packet); |
| |
| if (error == nullptr && this->_handshake_handler->is_completed() && !this->_handshake_handler->has_remote_tp()) { |
| error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::TRANSPORT_PARAMETER_ERROR); |
| } |
| } |
| } else { |
| if (!this->_alt_con_manager) { |
| this->_alt_con_manager = |
| new QUICAltConnectionManager(this, *this->_ctable, *this->_rtable, this->_peer_quic_connection_id, |
| this->_quic_config->instance_id(), this->_quic_config->active_cid_limit_out()); |
| this->_frame_generators.add_generator(*this->_alt_con_manager, QUICFrameGeneratorWeight::BEFORE_DATA); |
| this->_frame_dispatcher->add_handler(this->_alt_con_manager); |
| } |
| |
| this->_handshake_handler->update(packet); |
| |
| // on client side, _handshake_handler is already started. Just process packet like _state_handshake_process_handshake_packet() |
| error = this->_recv_and_ack(packet); |
| } |
| |
| return error; |
| } |
| |
| /** |
| This doesn't call this->_recv_and_ack(), because RETRY packet doesn't have any frames. |
| */ |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_handshake_process_retry_packet(const QUICRetryPacketR &packet) |
| { |
| ink_assert(this->netvc_context == NET_VCONNECTION_OUT); |
| |
| if (this->_av_token) { |
| QUICConDebug("Ignore RETRY packet - already processed before"); |
| return nullptr; |
| } |
| |
| // Check Integrity Tag |
| if (!packet.has_valid_tag(this->_original_quic_connection_id)) { |
| // Discard the packet |
| QUICConDebug("Ignore RETRY packet - integrity tag is not valid"); |
| return nullptr; |
| } else { |
| QUICConDebug("Integrity tag is valid"); |
| } |
| |
| // TODO: move packet->payload to _av_token |
| this->_av_token_len = packet.token().length(); |
| this->_av_token = ats_unique_malloc(this->_av_token_len); |
| memcpy(this->_av_token.get(), packet.token().buf(), this->_av_token_len); |
| |
| this->_padder->set_av_token_len(this->_av_token_len); |
| |
| // discard all transport state |
| this->_handshake_handler->reset(); |
| this->_handshake_handler->update(packet); |
| this->_packet_factory.reset(); |
| this->_loss_detector->reset(); |
| |
| this->_congestion_controller->reset(); |
| this->_packet_recv_queue.reset(); |
| |
| // Initialize Key Materials with peer CID. Because peer CID is DCID of (second) INITIAL packet from client which reply to RETRY |
| // packet from server |
| this->_hs_protocol->initialize_key_materials(this->_peer_quic_connection_id, packet.version()); |
| |
| // start handshake over |
| this->_handshake_handler->do_handshake(); |
| this->_schedule_packet_write_ready(); |
| |
| return nullptr; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_handshake_process_handshake_packet(const QUICHandshakePacketR &packet) |
| { |
| // Source address is verified by receiving any message from the client encrypted using the |
| // Handshake keys. |
| if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { |
| this->_verified_state.set_addr_verifed(); |
| } |
| return this->_recv_and_ack(packet); |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(const QUICZeroRttPacketR &packet) |
| { |
| this->_stream_manager->init_flow_control_params(this->_handshake_handler->local_transport_parameters(), |
| this->_handshake_handler->remote_transport_parameters()); |
| this->_start_application(); |
| return this->_recv_and_ack(packet); |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_connection_established_process_protected_packet(const QUICShortHeaderPacketR &packet) |
| { |
| QUICConnectionErrorUPtr error = nullptr; |
| bool has_non_probing_frame = false; |
| |
| error = this->_recv_and_ack(packet, &has_non_probing_frame); |
| if (error != nullptr) { |
| return error; |
| } |
| |
| // Migrate connection if needed |
| if (this->_alt_con_manager != nullptr) { |
| if (has_non_probing_frame && |
| (packet.destination_cid() != this->_quic_connection_id || !ats_ip_addr_port_eq(packet.from(), this->remote_addr))) { |
| error = this->_state_connection_established_migrate_connection(packet); |
| if (error != nullptr) { |
| return error; |
| } |
| } |
| } |
| |
| // For Connection Migration exercise |
| if (this->netvc_context == NET_VCONNECTION_OUT && this->_quic_config->cm_exercise_enabled()) { |
| this->_state_connection_established_initiate_connection_migration(); |
| } |
| |
| return error; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_connection_established_receive_packet() |
| { |
| QUICConnectionErrorUPtr error = nullptr; |
| QUICPacketCreationResult result; |
| |
| // Receive a QUIC packet |
| net_activity(this, this_ethread()); |
| do { |
| uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; |
| QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); |
| if (result == QUICPacketCreationResult::FAILED) { |
| // Don't make this error, and discard the packet. |
| // Because: |
| // - Attacker can terminate connections |
| // - It could be just an error on lower layer |
| continue; |
| } else if (result == QUICPacketCreationResult::NO_PACKET) { |
| return error; |
| } else if (result == QUICPacketCreationResult::NOT_READY) { |
| return error; |
| } else if (result == QUICPacketCreationResult::IGNORED) { |
| continue; |
| } |
| |
| // Process the packet |
| switch (packet->type()) { |
| case QUICPacketType::PROTECTED: |
| error = this->_state_connection_established_process_protected_packet(static_cast<QUICShortHeaderPacketR &>(*packet)); |
| break; |
| case QUICPacketType::INITIAL: |
| case QUICPacketType::HANDSHAKE: |
| case QUICPacketType::ZERO_RTT_PROTECTED: |
| // Pass packet to _recv_and_ack to send ack to the packet. Stream data will be discarded by offset mismatch. |
| error = this->_recv_and_ack(static_cast<QUICPacketR &>(*packet)); |
| break; |
| default: |
| QUICConDebug("Unknown packet type: %s(%" PRIu8 ")", QUICDebugNames::packet_type(packet->type()), |
| static_cast<uint8_t>(packet->type())); |
| |
| error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::INTERNAL_ERROR); |
| break; |
| } |
| |
| } while (error == nullptr && (result == QUICPacketCreationResult::SUCCESS || result == QUICPacketCreationResult::IGNORED)); |
| return error; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_closing_receive_packet() |
| { |
| while (this->_packet_recv_queue.size() > 0) { |
| QUICPacketCreationResult result; |
| uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; |
| QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); |
| if (result == QUICPacketCreationResult::SUCCESS) { |
| switch (packet->type()) { |
| case QUICPacketType::VERSION_NEGOTIATION: |
| // Ignore VN packets on closing state |
| break; |
| default: |
| this->_recv_and_ack(static_cast<QUICPacketR &>(*packet)); |
| break; |
| } |
| } |
| ++this->_state_closing_recv_packet_count; |
| |
| if (this->_state_closing_recv_packet_window < STATE_CLOSING_MAX_RECV_PKT_WIND && |
| this->_state_closing_recv_packet_count >= this->_state_closing_recv_packet_window) { |
| this->_state_closing_recv_packet_count = 0; |
| this->_state_closing_recv_packet_window <<= 1; |
| |
| this->_schedule_packet_write_ready(true); |
| break; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_draining_receive_packet() |
| { |
| while (this->_packet_recv_queue.size() > 0) { |
| QUICPacketCreationResult result; |
| uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; |
| QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); |
| if (result == QUICPacketCreationResult::SUCCESS) { |
| this->_recv_and_ack(static_cast<QUICPacketR &>(*packet)); |
| // Do NOT schedule WRITE_READY event from this point. |
| // An endpoint in the draining state MUST NOT send any packets. |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| /** |
| * 1. Check congestion window |
| * 2. Allocate buffer for UDP Payload |
| * 3. Generate QUIC Packet |
| * 4. Store data to the payload |
| * 5. Send UDP Packet |
| */ |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_common_send_packet() |
| { |
| uint32_t packet_count = 0; |
| uint32_t error = 0; |
| while (error == 0 && packet_count < PACKET_PER_EVENT) { |
| uint32_t window = this->_congestion_controller->credit(); |
| |
| if (window == 0) { |
| break; |
| } |
| |
| Ptr<IOBufferBlock> udp_payload(new_IOBufferBlock()); |
| uint32_t udp_payload_len = std::min(window, this->_pmtu); |
| udp_payload->alloc(iobuffer_size_to_index(udp_payload_len, BUFFER_SIZE_INDEX_32K)); |
| |
| uint32_t written = 0; |
| for (int i = static_cast<int>(this->_minimum_encryption_level); i <= static_cast<int>(QUICEncryptionLevel::ONE_RTT); ++i) { |
| uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; |
| |
| auto level = QUIC_ENCRYPTION_LEVELS[i]; |
| if (level == QUICEncryptionLevel::ONE_RTT && !this->_handshake_handler->is_completed()) { |
| continue; |
| } |
| |
| uint32_t max_packet_size = udp_payload_len - written; |
| if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { |
| max_packet_size = std::min(max_packet_size, this->_verified_state.windows()); |
| } |
| |
| QUICSentPacketInfoUPtr packet_info = std::make_unique<QUICSentPacketInfo>(); |
| QUICPacketUPtr packet = this->_packetize_frames(packet_buf, level, max_packet_size, packet_info->frames); |
| |
| if (packet) { |
| // trigger callback |
| this->_context->trigger(QUICContext::CallbackEvent::PACKET_SEND, packet.get()); |
| |
| packet_info->packet_number = packet->packet_number(); |
| packet_info->time_sent = Thread::get_hrtime(); |
| packet_info->ack_eliciting = packet->is_ack_eliciting(); |
| packet_info->in_flight = true; |
| if (packet_info->ack_eliciting) { |
| packet_info->sent_bytes = packet->size(); |
| } else { |
| packet_info->sent_bytes = 0; |
| } |
| packet_info->type = packet->type(); |
| packet_info->pn_space = QUICTypeUtil::pn_space(level); |
| |
| if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { |
| QUICConDebug("send to unverified window: %u", this->_verified_state.windows()); |
| this->_verified_state.consume(packet->size()); |
| } |
| |
| // TODO: do not write two QUIC Short Header Packets |
| uint8_t *buf = reinterpret_cast<uint8_t *>(udp_payload->end()); |
| size_t len = 0; |
| packet->store(buf, &len); |
| udp_payload->fill(len); |
| written += len; |
| |
| int dcil = (this->_peer_quic_connection_id == QUICConnectionId::ZERO()) ? 0 : this->_peer_quic_connection_id.length(); |
| if (!this->_ph_protector.protect(buf, len, dcil)) { |
| ink_assert(!"failed to protect buffer"); |
| } |
| |
| QUICConVDebug("[TX] %s packet #%" PRIu64 " size=%zu", QUICDebugNames::packet_type(packet->type()), packet->packet_number(), |
| len); |
| |
| if (this->_pp_key_info.is_encryption_key_available(QUICKeyPhase::INITIAL) && packet->type() == QUICPacketType::HANDSHAKE && |
| this->netvc_context == NET_VCONNECTION_OUT) { |
| this->_pp_key_info.drop_keys(QUICKeyPhase::INITIAL); |
| this->_loss_detector->on_packet_number_space_discarded(QUICPacketNumberSpace::INITIAL); |
| this->_minimum_encryption_level = QUICEncryptionLevel::HANDSHAKE; |
| } |
| |
| this->_loss_detector->on_packet_sent(std::move(packet_info)); |
| packet_count++; |
| } |
| } |
| |
| if (written) { |
| this->_packet_handler->send_packet(this, udp_payload); |
| } else { |
| udp_payload->dealloc(); |
| break; |
| } |
| } |
| |
| if (packet_count) { |
| this->_context->trigger(QUICContext::CallbackEvent::METRICS_UPDATE, this->_congestion_controller->congestion_window(), |
| this->_congestion_controller->bytes_in_flight(), this->_congestion_controller->current_ssthresh()); |
| |
| QUIC_INCREMENT_DYN_STAT_EX(QUICStats::total_packets_sent_stat, packet_count); |
| net_activity(this, this_ethread()); |
| } |
| |
| return nullptr; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_closing_send_packet() |
| { |
| this->_packetize_closing_frame(); |
| |
| // TODO: should credit of congestion controller be checked? |
| |
| // During the closing period, an endpoint that sends a |
| // closing frame SHOULD respond to any packet that it receives with |
| // another packet containing a closing frame. To minimize the state |
| // that an endpoint maintains for a closing connection, endpoints MAY |
| // send the exact same packet. |
| if (this->_the_final_packet) { |
| this->_packet_handler->send_packet(*this->_the_final_packet, this, this->_ph_protector); |
| } |
| |
| return nullptr; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICNetVConnection::_store_frame(Ptr<IOBufferBlock> parent_block, size_t &size_added, uint64_t &max_frame_size, QUICFrame &frame, |
| std::vector<QUICSentPacketInfo::FrameInfo> &frames) |
| { |
| Ptr<IOBufferBlock> new_block = frame.to_io_buffer_block(max_frame_size); |
| |
| size_added = 0; |
| for (Ptr<IOBufferBlock> tmp = new_block; tmp; tmp = tmp->next) { |
| size_added += tmp->size(); |
| } |
| |
| if (parent_block == nullptr) { |
| parent_block = new_block; |
| } else { |
| parent_block->next = new_block; |
| } |
| |
| // frame should be stored because it's created with max_frame_size |
| ink_assert(size_added != 0); |
| |
| max_frame_size -= size_added; |
| |
| if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { |
| char msg[1024]; |
| frame.debug_msg(msg, sizeof(msg)); |
| QUICConVDebug("[TX] | %s", msg); |
| } |
| |
| frames.emplace_back(frame.id(), frame.generated_by()); |
| |
| while (parent_block->next) { |
| parent_block = parent_block->next; |
| } |
| return parent_block; |
| } |
| |
| QUICPacketUPtr |
| QUICNetVConnection::_packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel level, uint64_t max_packet_size, |
| std::vector<QUICSentPacketInfo::FrameInfo> &frames) |
| { |
| QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); |
| if (max_packet_size <= MAX_PACKET_OVERHEAD) { |
| return packet; |
| } |
| |
| // TODO: adjust MAX_PACKET_OVERHEAD for each encryption level |
| uint64_t max_frame_size = max_packet_size - MAX_PACKET_OVERHEAD; |
| if (level == QUICEncryptionLevel::INITIAL && this->_av_token) { |
| max_frame_size = max_frame_size - (QUICVariableInt::size(this->_av_token_len) + this->_av_token_len); |
| } |
| max_frame_size = std::min(max_frame_size, this->_maximum_stream_frame_data_size()); |
| |
| bool probing = false; |
| int frame_count = 0; |
| size_t len = 0; |
| Ptr<IOBufferBlock> first_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| Ptr<IOBufferBlock> last_block = first_block; |
| first_block->alloc(iobuffer_size_to_index(0, BUFFER_SIZE_INDEX_32K)); |
| first_block->fill(0); |
| |
| uint32_t seq_num = this->_seq_num++; |
| size_t size_added = 0; |
| bool ack_eliciting = false; |
| bool crypto = false; |
| uint8_t frame_instance_buffer[QUICFrame::MAX_INSTANCE_SIZE]; // This is for a frame instance but not serialized frame data |
| QUICFrame *frame = nullptr; |
| for (auto g : this->_frame_generators.generators()) { |
| // a non-ack_eliciting packet is ready, but we can not send continuous two ack_eliciting packets. |
| while (g->will_generate_frame(level, len, ack_eliciting, seq_num)) { |
| // FIXME will_generate_frame should receive more parameters so we don't need extra checks |
| if (g == this->_remote_flow_controller && !this->_stream_manager->will_generate_frame(level, len, ack_eliciting, seq_num)) { |
| break; |
| } |
| |
| // Common block |
| frame = |
| g->generate_frame(frame_instance_buffer, level, this->_remote_flow_controller->credit(), max_frame_size, len, seq_num); |
| if (frame) { |
| this->_context->trigger(QUICContext::CallbackEvent::FRAME_PACKETIZE, *frame); |
| // Some frame types must not be sent on Initial and Handshake packets |
| switch (auto t = frame->type(); level) { |
| case QUICEncryptionLevel::INITIAL: |
| case QUICEncryptionLevel::HANDSHAKE: |
| ink_assert(t == QUICFrameType::CRYPTO || t == QUICFrameType::ACK || t == QUICFrameType::PADDING || |
| t == QUICFrameType::CONNECTION_CLOSE || t == QUICFrameType::PING); |
| break; |
| default: |
| break; |
| } |
| |
| ++frame_count; |
| probing |= frame->is_probing_frame(); |
| if (frame->is_flow_controlled()) { |
| int ret = this->_remote_flow_controller->update(this->_stream_manager->total_offset_sent()); |
| QUICFCVDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller->current_offset(), |
| this->_remote_flow_controller->current_limit()); |
| ink_assert(ret == 0); |
| } |
| last_block = this->_store_frame(last_block, size_added, max_frame_size, *frame, frames); |
| len += size_added; |
| |
| // FIXME ACK frame should have priority |
| if (frame->type() == QUICFrameType::STREAM) { |
| if (++this->_stream_frames_sent % MAX_CONSECUTIVE_STREAMS == 0) { |
| break; |
| } |
| } |
| |
| if (!ack_eliciting && frame->ack_eliciting()) { |
| ack_eliciting = true; |
| } |
| |
| if (frame->type() == QUICFrameType::CRYPTO && frame->ack_eliciting()) { |
| crypto = true; |
| } |
| |
| frame->~QUICFrame(); |
| } else { |
| // Move to next generator |
| break; |
| } |
| } |
| } |
| |
| // Schedule a packet |
| if (len != 0) { |
| // Packet is retransmittable if it's not ack only packet |
| packet = this->_build_packet(packet_buf, level, first_block, ack_eliciting, probing, crypto); |
| } |
| |
| return packet; |
| } |
| |
| void |
| QUICNetVConnection::_packetize_closing_frame() |
| { |
| if (this->_connection_error == nullptr || this->_the_final_packet) { |
| return; |
| } |
| |
| QUICFrame *frame = nullptr; |
| |
| // CONNECTION_CLOSE |
| uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; |
| frame = QUICFrameFactory::create_connection_close_frame(frame_buf, *this->_connection_error); |
| |
| uint32_t max_size = this->_maximum_quic_packet_size(); |
| |
| size_t size_added = 0; |
| uint64_t max_frame_size = static_cast<uint64_t>(max_size); |
| std::vector<QUICSentPacketInfo::FrameInfo> frames; |
| Ptr<IOBufferBlock> first_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| Ptr<IOBufferBlock> last_block = first_block; |
| first_block->alloc(iobuffer_size_to_index(0, BUFFER_SIZE_INDEX_32K)); |
| first_block->fill(0); |
| last_block = this->_store_frame(last_block, size_added, max_frame_size, *frame, frames); |
| |
| QUICEncryptionLevel level = this->_hs_protocol->current_encryption_level(); |
| ink_assert(level != QUICEncryptionLevel::ZERO_RTT); |
| this->_the_final_packet = this->_build_packet(this->_final_packet_buf, level, first_block, true, false, false); |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_recv_and_ack(const QUICPacketR &packet, bool *has_non_probing_frame) |
| { |
| ink_assert(packet.type() != QUICPacketType::RETRY); |
| |
| uint16_t size = packet.payload_length(); |
| ats_unique_buf payload_ubuf = ats_unique_malloc(size); |
| uint8_t *payload = payload_ubuf.get(); |
| size_t copied_len = 0; |
| for (auto b = packet.payload_block(); b; b = b->next) { |
| memcpy(payload + copied_len, b->start(), b->size()); |
| copied_len += b->size(); |
| } |
| QUICPacketNumber packet_num = packet.packet_number(); |
| QUICEncryptionLevel level = QUICTypeUtil::encryption_level(packet.type()); |
| |
| bool ack_only; |
| bool is_flow_controlled; |
| |
| QUICConnectionErrorUPtr error = nullptr; |
| if (has_non_probing_frame) { |
| *has_non_probing_frame = false; |
| } |
| |
| error = this->_frame_dispatcher->receive_frames(*this->_context, level, payload, size, ack_only, is_flow_controlled, |
| has_non_probing_frame, static_cast<const QUICPacketR *>(&packet)); |
| this->_context->trigger(QUICContext::CallbackEvent::PACKET_RECV, &packet); |
| |
| if (error != nullptr) { |
| return error; |
| } |
| |
| if (is_flow_controlled) { |
| int ret = this->_local_flow_controller->update(this->_stream_manager->total_offset_received()); |
| QUICFCVDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller->current_offset(), |
| this->_local_flow_controller->current_limit()); |
| |
| if (ret != 0) { |
| return std::make_unique<QUICConnectionError>(QUICTransErrorCode::FLOW_CONTROL_ERROR); |
| } |
| |
| this->_local_flow_controller->forward_limit(this->_stream_manager->total_reordered_bytes() + this->_flow_control_buffer_size); |
| QUICFCVDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller->current_offset(), |
| this->_local_flow_controller->current_limit()); |
| } |
| |
| this->_ack_frame_manager.update(level, packet_num, size, ack_only); |
| |
| return error; |
| } |
| |
| QUICPacketUPtr |
| QUICNetVConnection::_build_packet(uint8_t *packet_buf, QUICEncryptionLevel level, const Ptr<IOBufferBlock> &parent_block, |
| bool ack_eliciting, bool probing, bool crypto) |
| { |
| QUICPacketType type = QUICTypeUtil::packet_type(level); |
| QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); |
| |
| size_t len = 0; |
| for (Ptr<IOBufferBlock> tmp = parent_block; tmp; tmp = tmp->next) { |
| len += tmp->size(); |
| } |
| |
| switch (type) { |
| case QUICPacketType::INITIAL: { |
| QUICConnectionId dcid = this->_peer_quic_connection_id; |
| ats_unique_buf token = {nullptr}; |
| size_t token_len = 0; |
| |
| if (this->netvc_context == NET_VCONNECTION_OUT) { |
| // TODO: Add a case of using token which is advertised by NEW_TOKEN frame |
| if (this->_av_token) { |
| token = ats_unique_malloc(this->_av_token_len); |
| token_len = this->_av_token_len; |
| memcpy(token.get(), this->_av_token.get(), token_len); |
| } else { |
| dcid = this->_original_quic_connection_id; |
| } |
| } |
| |
| packet = this->_packet_factory.create_initial_packet( |
| packet_buf, dcid, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::INITIAL), parent_block, |
| len, ack_eliciting, probing, crypto, std::move(token), token_len); |
| break; |
| } |
| case QUICPacketType::HANDSHAKE: { |
| packet = this->_packet_factory.create_handshake_packet(packet_buf, this->_peer_quic_connection_id, this->_quic_connection_id, |
| this->_largest_acked_packet_number(QUICEncryptionLevel::HANDSHAKE), |
| parent_block, len, ack_eliciting, probing, crypto); |
| break; |
| } |
| case QUICPacketType::ZERO_RTT_PROTECTED: { |
| packet = this->_packet_factory.create_zero_rtt_packet(packet_buf, this->_original_quic_connection_id, this->_quic_connection_id, |
| this->_largest_acked_packet_number(QUICEncryptionLevel::ZERO_RTT), |
| parent_block, len, ack_eliciting, probing); |
| break; |
| } |
| case QUICPacketType::PROTECTED: { |
| packet = this->_packet_factory.create_short_header_packet(packet_buf, this->_peer_quic_connection_id, |
| this->_largest_acked_packet_number(QUICEncryptionLevel::ONE_RTT), |
| parent_block, len, ack_eliciting, probing); |
| break; |
| } |
| default: |
| // should not be here |
| ink_assert(false); |
| break; |
| } |
| |
| return packet; |
| } |
| |
| void |
| QUICNetVConnection::_init_flow_control_params(const std::shared_ptr<const QUICTransportParameters> &local_tp, |
| const std::shared_ptr<const QUICTransportParameters> &remote_tp) |
| { |
| this->_stream_manager->init_flow_control_params(local_tp, remote_tp); |
| |
| uint64_t local_initial_max_data = 0; |
| uint64_t remote_initial_max_data = 0; |
| if (local_tp) { |
| local_initial_max_data = local_tp->getAsUInt(QUICTransportParameterId::INITIAL_MAX_DATA); |
| this->_flow_control_buffer_size = local_initial_max_data; |
| } |
| if (remote_tp) { |
| remote_initial_max_data = remote_tp->getAsUInt(QUICTransportParameterId::INITIAL_MAX_DATA); |
| } |
| |
| this->_local_flow_controller->set_limit(local_initial_max_data); |
| this->_remote_flow_controller->set_limit(remote_initial_max_data); |
| QUICFCDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller->current_offset(), |
| this->_local_flow_controller->current_limit()); |
| QUICFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller->current_offset(), |
| this->_remote_flow_controller->current_limit()); |
| } |
| |
| void |
| QUICNetVConnection::_handle_error(QUICConnectionErrorUPtr error) |
| { |
| QUICError("QUICError: %s (%u), %s (0x%" PRIx16 ")", QUICDebugNames::error_class(error->cls), |
| static_cast<unsigned int>(error->cls), QUICDebugNames::error_code(error->code), error->code); |
| |
| // Connection Error |
| this->close_quic_connection(std::move(error)); |
| } |
| |
| QUICPacketUPtr |
| QUICNetVConnection::_dequeue_recv_packet(uint8_t *packet_buf, QUICPacketCreationResult &result) |
| { |
| QUICPacketUPtr packet = this->_packet_recv_queue.dequeue(packet_buf, result); |
| |
| if (result == QUICPacketCreationResult::SUCCESS) { |
| if (this->direction() == NET_VCONNECTION_OUT) { |
| // Reset CID if a server sent back a new CID |
| // FIXME This should happen only once - it should probably be controlled by PathManager |
| if (packet->type() != QUICPacketType::PROTECTED && (packet->type() != QUICPacketType::RETRY || !this->_av_token)) { |
| QUICConnectionId src_cid = static_cast<QUICLongHeaderPacketR &>(*packet).source_cid(); |
| // FIXME src connection id could be zero ? if so, check packet header type. |
| if (src_cid != QUICConnectionId::ZERO()) { |
| if (this->_peer_quic_connection_id != src_cid) { |
| this->_update_peer_cid(src_cid); |
| } |
| } |
| } |
| } |
| |
| if (!this->_verified_state.is_verified()) { |
| this->_verified_state.fill(packet->size()); |
| } |
| } |
| |
| // Debug prints |
| switch (result) { |
| case QUICPacketCreationResult::NO_PACKET: |
| break; |
| case QUICPacketCreationResult::NOT_READY: |
| QUICConDebug("Not ready to decrypt the packet"); |
| break; |
| case QUICPacketCreationResult::IGNORED: |
| QUICConDebug("Ignored"); |
| break; |
| case QUICPacketCreationResult::UNSUPPORTED: |
| QUICConDebug("Unsupported version"); |
| break; |
| case QUICPacketCreationResult::SUCCESS: |
| switch (packet->type()) { |
| case QUICPacketType::VERSION_NEGOTIATION: |
| case QUICPacketType::RETRY: |
| QUICConVDebug("[RX] %s packet size=%u", QUICDebugNames::packet_type(packet->type()), packet->size()); |
| break; |
| default: |
| QUICConVDebug("[RX] %s packet #%" PRIu64 " size=%u header_len=%u payload_len=%u", QUICDebugNames::packet_type(packet->type()), |
| packet->packet_number(), packet->size(), packet->header_size(), packet->payload_length()); |
| break; |
| } |
| break; |
| default: |
| QUICConDebug("Failed to decrypt the packet"); |
| break; |
| } |
| |
| return packet; |
| } |
| |
| void |
| QUICNetVConnection::_schedule_packet_write_ready(bool delay) |
| { |
| if (!this->_packet_write_ready) { |
| QUICConVVVDebug("Schedule %s event", QUICDebugNames::quic_event(QUIC_EVENT_PACKET_WRITE_READY)); |
| if (delay) { |
| this->_packet_write_ready = this->thread->schedule_in(this, WRITE_READY_INTERVAL, QUIC_EVENT_PACKET_WRITE_READY, nullptr); |
| } else { |
| this->_packet_write_ready = this->thread->schedule_imm(this, QUIC_EVENT_PACKET_WRITE_READY, nullptr); |
| } |
| } |
| } |
| |
| void |
| QUICNetVConnection::_unschedule_packet_write_ready() |
| { |
| if (this->_packet_write_ready) { |
| this->_packet_write_ready->cancel(); |
| this->_packet_write_ready = nullptr; |
| } |
| } |
| |
| void |
| QUICNetVConnection::_close_packet_write_ready(Event *data) |
| { |
| ink_assert(this->_packet_write_ready == data); |
| this->_packet_write_ready = nullptr; |
| } |
| |
| void |
| QUICNetVConnection::_schedule_closing_timeout(ink_hrtime interval) |
| { |
| if (!this->_closing_timeout) { |
| QUICConDebug("Schedule %s event in %" PRIu64 "ms", QUICDebugNames::quic_event(QUIC_EVENT_CLOSING_TIMEOUT), |
| interval / HRTIME_MSECOND); |
| this->_closing_timeout = this->thread->schedule_in_local(this, interval, QUIC_EVENT_CLOSING_TIMEOUT); |
| } |
| } |
| |
| void |
| QUICNetVConnection::_unschedule_closing_timeout() |
| { |
| if (this->_closing_timeout) { |
| QUICConDebug("Unschedule %s event", QUICDebugNames::quic_event(QUIC_EVENT_CLOSING_TIMEOUT)); |
| this->_closing_timeout->cancel(); |
| this->_closing_timeout = nullptr; |
| } |
| } |
| |
| void |
| QUICNetVConnection::_schedule_ack_manager_periodic(ink_hrtime interval) |
| { |
| this->_ack_manager_periodic = this->thread->schedule_every(this, interval, QUIC_EVENT_ACK_PERIODIC); |
| } |
| |
| void |
| QUICNetVConnection::_unschedule_ack_manager_periodic() |
| { |
| if (this->_ack_manager_periodic) { |
| QUICConDebug("Unschedule %s event", QUICDebugNames::quic_event(QUIC_EVENT_ACK_PERIODIC)); |
| this->_ack_manager_periodic->cancel(); |
| this->_ack_manager_periodic = nullptr; |
| } |
| } |
| |
| void |
| QUICNetVConnection::_close_closing_timeout(Event *data) |
| { |
| ink_assert(this->_closing_timeout == data); |
| this->_closing_timeout = nullptr; |
| } |
| |
| void |
| QUICNetVConnection::_schedule_closed_event() |
| { |
| if (!this->_closed_event) { |
| QUICConDebug("Schedule %s event", QUICDebugNames::quic_event(QUIC_EVENT_SHUTDOWN)); |
| this->_closed_event = this->thread->schedule_imm(this, QUIC_EVENT_SHUTDOWN, nullptr); |
| } |
| } |
| |
| void |
| QUICNetVConnection::_unschedule_closed_event() |
| { |
| if (!this->_closed_event) { |
| QUICConDebug("Unschedule %s event", QUICDebugNames::quic_event(QUIC_EVENT_SHUTDOWN)); |
| this->_closed_event->cancel(); |
| this->_closed_event = nullptr; |
| } |
| } |
| |
| void |
| QUICNetVConnection::_close_closed_event(Event *data) |
| { |
| ink_assert(this->_closed_event == data); |
| this->_closed_event = nullptr; |
| } |
| |
| int |
| QUICNetVConnection::_complete_handshake_if_possible() |
| { |
| if (this->handler != reinterpret_cast<NetVConnHandler>(&QUICNetVConnection::state_handshake)) { |
| return 0; |
| } |
| |
| if (!(this->_handshake_handler && this->_handshake_handler->is_completed())) { |
| return -1; |
| } |
| |
| if (this->netvc_context == NET_VCONNECTION_OUT && !this->_handshake_handler->has_remote_tp()) { |
| return -1; |
| } |
| |
| this->_init_flow_control_params(this->_handshake_handler->local_transport_parameters(), |
| this->_handshake_handler->remote_transport_parameters()); |
| |
| uint64_t ack_delay_exponent = |
| this->_handshake_handler->remote_transport_parameters()->getAsUInt(QUICTransportParameterId::ACK_DELAY_EXPONENT); |
| this->_loss_detector->update_ack_delay_exponent(ack_delay_exponent); |
| |
| uint64_t max_ack_delay = |
| this->_handshake_handler->remote_transport_parameters()->getAsUInt(QUICTransportParameterId::MAX_ACK_DELAY); |
| this->_rtt_measure.set_max_ack_delay(max_ack_delay); |
| |
| const uint8_t *reset_token; |
| uint16_t reset_token_len; |
| reset_token = this->_handshake_handler->remote_transport_parameters()->getAsBytes(QUICTransportParameterId::STATELESS_RESET_TOKEN, |
| reset_token_len); |
| if (reset_token) { |
| this->_rtable->insert({reset_token}, this); |
| } |
| |
| this->_start_application(); |
| |
| this->_handshake_completed = true; |
| |
| return 0; |
| } |
| |
| void |
| QUICNetVConnection::_start_application() |
| { |
| if (!this->_application_started) { |
| this->_application_started = true; |
| |
| const uint8_t *app_name; |
| unsigned int app_name_len = 0; |
| this->_handshake_handler->negotiated_application_name(&app_name, &app_name_len); |
| if (app_name == nullptr) { |
| app_name = reinterpret_cast<const uint8_t *>(IP_PROTO_TAG_HTTP_QUIC.data()); |
| app_name_len = IP_PROTO_TAG_HTTP_QUIC.size(); |
| } |
| |
| this->set_negotiated_protocol_id({reinterpret_cast<const char *>(app_name), static_cast<size_t>(app_name_len)}); |
| |
| if (netvc_context == NET_VCONNECTION_IN) { |
| if (!this->setSelectedProtocol(app_name, app_name_len)) { |
| this->_handle_error(std::make_unique<QUICConnectionError>(QUICTransErrorCode::PROTOCOL_VIOLATION)); |
| } else { |
| this->endpoint()->handleEvent(NET_EVENT_ACCEPT, this); |
| } |
| } else { |
| this->action_.continuation->handleEvent(NET_EVENT_OPEN, this); |
| } |
| } |
| } |
| |
| void |
| QUICNetVConnection::_switch_to_handshake_state() |
| { |
| QUICConDebug("Enter state_handshake"); |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_handshake); |
| } |
| |
| void |
| QUICNetVConnection::_switch_to_established_state() |
| { |
| if (this->_complete_handshake_if_possible() == 0) { |
| QUICConDebug("Enter state_connection_established"); |
| QUICConDebug("Negotiated cipher suite: %s", this->_handshake_handler->negotiated_cipher_suite()); |
| this->_record_tls_handshake_end_time(); |
| |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_connection_established); |
| |
| std::shared_ptr<const QUICTransportParameters> remote_tp = this->_handshake_handler->remote_transport_parameters(); |
| std::shared_ptr<const QUICTransportParameters> local_tp = this->_handshake_handler->local_transport_parameters(); |
| |
| uint64_t active_cid_limit = remote_tp->getAsUInt(QUICTransportParameterId::ACTIVE_CONNECTION_ID_LIMIT); |
| if (active_cid_limit) { |
| this->_alt_con_manager->set_remote_active_cid_limit(active_cid_limit); |
| } |
| |
| this->set_inactivity_timeout(HRTIME_MSECONDS(std::min(remote_tp->getAsUInt(QUICTransportParameterId::MAX_IDLE_TIMEOUT), |
| local_tp->getAsUInt(QUICTransportParameterId::MAX_IDLE_TIMEOUT)))); |
| |
| if (this->direction() == NET_VCONNECTION_OUT) { |
| uint16_t len; |
| const uint8_t *pref_addr_buf = remote_tp->getAsBytes(QUICTransportParameterId::PREFERRED_ADDRESS, len); |
| if (pref_addr_buf) { |
| this->_alt_con_manager->set_remote_preferred_address({pref_addr_buf, len}); |
| } |
| } else { |
| QUICPath trusted_path = {this->local_addr, this->remote_addr}; |
| this->_path_manager->set_trusted_path(trusted_path); |
| } |
| } else { |
| // Illegal state change |
| ink_assert(!"Handshake has to be completed"); |
| } |
| } |
| |
| void |
| QUICNetVConnection::_switch_to_closing_state(QUICConnectionErrorUPtr error) |
| { |
| if (this->_complete_handshake_if_possible() != 0) { |
| QUICConDebug("Switching state without handshake completion"); |
| } |
| if (error->msg) { |
| QUICConDebug("Reason: %.*s", static_cast<int>(strlen(error->msg)), error->msg); |
| } |
| |
| // Once we are in closing or draining state, the ack_manager is not needed anymore. Because we don't send |
| // any frame other than close_frame. |
| this->_unschedule_ack_manager_periodic(); |
| |
| this->_connection_error = std::move(error); |
| this->_schedule_packet_write_ready(); |
| |
| this->remove_from_active_queue(); |
| this->set_inactivity_timeout(0); |
| |
| ink_hrtime rto = this->_rtt_measure.current_pto_period(); |
| |
| QUICConDebug("Enter state_connection_closing"); |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_connection_closing); |
| |
| // This states SHOULD persist for three times the |
| // current Retransmission Timeout (RTO) interval as defined in |
| // [QUIC-RECOVERY]. |
| this->_schedule_closing_timeout(3 * rto); |
| } |
| |
| void |
| QUICNetVConnection::_switch_to_draining_state(QUICConnectionErrorUPtr error) |
| { |
| if (this->_complete_handshake_if_possible() != 0) { |
| QUICConDebug("Switching state without handshake completion"); |
| } |
| if (error->msg) { |
| QUICConDebug("Reason: %.*s", static_cast<int>(strlen(error->msg)), error->msg); |
| } |
| |
| // Once we are in closing or draining state, the ack_manager is not needed anymore. Because we don't send |
| // any frame other than close_frame. |
| this->_unschedule_ack_manager_periodic(); |
| |
| this->remove_from_active_queue(); |
| this->set_inactivity_timeout(0); |
| |
| ink_hrtime rto = this->_rtt_measure.current_pto_period(); |
| |
| QUICConDebug("Enter state_connection_draining"); |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_connection_draining); |
| |
| // This states SHOULD persist for three times the |
| // current Retransmission Timeout (RTO) interval as defined in |
| // [QUIC-RECOVERY]. |
| |
| this->_schedule_closing_timeout(3 * rto); |
| } |
| |
| void |
| QUICNetVConnection::_switch_to_close_state() |
| { |
| this->_unschedule_closing_timeout(); |
| |
| if (this->_complete_handshake_if_possible() != 0) { |
| QUICConDebug("Switching state without handshake completion"); |
| } |
| QUICConDebug("Enter state_connection_closed"); |
| SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_connection_closed); |
| this->_schedule_closed_event(); |
| } |
| |
| void |
| QUICNetVConnection::_handle_idle_timeout() |
| { |
| this->remove_from_active_queue(); |
| this->_switch_to_draining_state(std::make_unique<QUICConnectionError>(QUICTransErrorCode::NO_ERROR, "Idle Timeout")); |
| |
| // TODO: signal VC_EVENT_ACTIVE_TIMEOUT/VC_EVENT_INACTIVITY_TIMEOUT to application |
| } |
| |
| void |
| QUICNetVConnection::_handle_active_timeout() |
| { |
| this->close_quic_connection(std::make_unique<QUICConnectionError>(QUICTransErrorCode::NO_ERROR, "Active Timeout")); |
| } |
| |
| void |
| QUICNetVConnection::_validate_new_path(const QUICPath &path) |
| { |
| // Not sure how long we should wait. The spec says just "enough time". |
| // Use the same time amount as the closing timeout. |
| ink_hrtime rto = this->_rtt_measure.current_pto_period(); |
| this->_path_manager->open_new_path(path, 3 * rto); |
| } |
| |
| void |
| QUICNetVConnection::_update_cids() |
| { |
| snprintf(this->_cids_data, sizeof(this->_cids_data), "%08" PRIx32 "-%08" PRIx32 "", this->_peer_quic_connection_id.h32(), |
| this->_quic_connection_id.h32()); |
| |
| this->_cids = {this->_cids_data, sizeof(this->_cids_data)}; |
| } |
| |
| void |
| QUICNetVConnection::_update_peer_cid(const QUICConnectionId &new_cid) |
| { |
| if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { |
| QUICConDebug("update peer dcid: %s -> %s", this->_peer_quic_connection_id.hex().c_str(), new_cid.hex().c_str()); |
| } |
| |
| this->_peer_old_quic_connection_id = this->_peer_quic_connection_id; |
| this->_peer_quic_connection_id = new_cid; |
| this->_update_cids(); |
| } |
| |
| void |
| QUICNetVConnection::_update_local_cid(const QUICConnectionId &new_cid) |
| { |
| if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { |
| QUICConDebug("update local dcid: %s -> %s", this->_quic_connection_id.hex().c_str(), new_cid.hex().c_str()); |
| } |
| |
| this->_quic_connection_id = new_cid; |
| this->_update_cids(); |
| } |
| |
| void |
| QUICNetVConnection::_rerandomize_original_cid() |
| { |
| QUICConnectionId tmp = this->_original_quic_connection_id; |
| this->_original_quic_connection_id.randomize(); |
| |
| if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { |
| QUICConDebug("original cid: %s -> %s", tmp.hex().c_str(), this->_original_quic_connection_id.hex().c_str()); |
| } |
| } |
| |
| QUICHandshakeProtocol * |
| QUICNetVConnection::_setup_handshake_protocol(const shared_SSL_CTX &ctx) |
| { |
| // Initialize handshake protocol specific stuff |
| // For QUICv1 TLS is the only option |
| QUICTLS *tls = new QUICTLS(this->_pp_key_info, ctx.get(), this->direction(), this->options, |
| this->_quic_config->client_session_file(), this->_quic_config->client_keylog_file()); |
| SSL_set_ex_data(tls->ssl_handle(), QUIC::ssl_quic_qc_index, static_cast<QUICConnection *>(this)); |
| TLSBasicSupport::bind(tls->ssl_handle(), this); |
| TLSSessionResumptionSupport::bind(tls->ssl_handle(), this); |
| ALPNSupport::bind(tls->ssl_handle(), this); |
| |
| return tls; |
| } |
| |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_connection_established_migrate_connection(const QUICPacketR &p) |
| { |
| ink_assert(this->_handshake_handler->is_completed()); |
| |
| QUICConnectionErrorUPtr error = nullptr; |
| QUICConnectionId dcid = p.destination_cid(); |
| |
| if (this->netvc_context == NET_VCONNECTION_IN) { |
| QUICConDebug("Connection migration is initiated by remote"); |
| } |
| |
| if (this->connection_id() == dcid) { |
| QUICConDebug("Maybe NAT rebinding"); |
| // On client side (NET_VCONNECTION_OUT), nothing to do any more |
| if (this->netvc_context == NET_VCONNECTION_IN) { |
| Connection con; |
| con.setRemote(&(p.from().sa)); |
| this->con.move(con); |
| this->set_remote_addr(); |
| this->_udp_con = p.udp_con(); |
| this->_packet_handler = static_cast<QUICPacketHandlerIn *>( |
| static_cast<NetAccept *>(static_cast<UnixUDPConnection *>(this->_udp_con)->continuation)); |
| |
| QUICPath new_path = {p.to(), p.from()}; |
| this->_validate_new_path(new_path); |
| } |
| } else { |
| QUICConDebug("Different CID"); |
| if (!this->_alt_con_manager->is_ready_to_migrate()) { |
| QUICConDebug("Ignore connection migration - remote endpoint initiated CM before sending NEW_CONNECTION_ID frames"); |
| return error; |
| } |
| |
| if (this->_alt_con_manager->migrate_to(dcid, this->_reset_token)) { |
| // DCID of received packet is local cid |
| this->_update_local_cid(dcid); |
| |
| // On client side (NET_VCONNECTION_OUT), nothing to do any more |
| if (this->netvc_context == NET_VCONNECTION_IN) { |
| Connection con; |
| con.setRemote(&(p.from().sa)); |
| this->con.move(con); |
| this->set_remote_addr(); |
| this->_udp_con = p.udp_con(); |
| this->_packet_handler = static_cast<QUICPacketHandlerIn *>( |
| static_cast<NetAccept *>(static_cast<UnixUDPConnection *>(this->_udp_con)->continuation)); |
| |
| this->_update_peer_cid(this->_alt_con_manager->migrate_to_alt_cid()); |
| |
| QUICPath new_path = {p.to(), p.from()}; |
| this->_validate_new_path(new_path); |
| } |
| } else { |
| QUICConDebug("Connection migration failed cid=%s", dcid.hex().c_str()); |
| } |
| } |
| |
| return error; |
| } |
| |
| /** |
| * Connection Migration Exercise from client |
| */ |
| QUICConnectionErrorUPtr |
| QUICNetVConnection::_state_connection_established_initiate_connection_migration() |
| { |
| ink_assert(this->_handshake_handler->is_completed()); |
| ink_assert(this->netvc_context == NET_VCONNECTION_OUT); |
| |
| QUICConnectionErrorUPtr error = nullptr; |
| |
| std::shared_ptr<const QUICTransportParameters> remote_tp = this->_handshake_handler->remote_transport_parameters(); |
| |
| if (this->_connection_migration_initiated || remote_tp->contains(QUICTransportParameterId::DISABLE_ACTIVE_MIGRATION) || |
| !this->_alt_con_manager->is_ready_to_migrate() || |
| this->_alt_con_manager->will_generate_frame(QUICEncryptionLevel::ONE_RTT, 0, true, this->_seq_num++)) { |
| return error; |
| } |
| |
| QUICConDebug("Initiated connection migration"); |
| this->_connection_migration_initiated = true; |
| |
| this->_update_peer_cid(this->_alt_con_manager->migrate_to_alt_cid()); |
| |
| auto current_path = this->_path_manager->get_verified_path(); |
| QUICPath new_path = {current_path.local_ep(), current_path.remote_ep()}; |
| this->_validate_new_path(new_path); |
| |
| return error; |
| } |
| |
| void |
| QUICNetVConnection::_handle_periodic_ack_event() |
| { |
| bool need_schedule = false; |
| for (int i = static_cast<int>(this->_minimum_encryption_level); i <= static_cast<int>(QUICEncryptionLevel::ONE_RTT); ++i) { |
| if (this->_ack_frame_manager.will_generate_frame(QUIC_ENCRYPTION_LEVELS[i], 0, true, this->_seq_num++)) { |
| need_schedule = true; |
| break; |
| } |
| } |
| |
| if (need_schedule) { |
| // we have ack to send |
| // FIXME: should sent depend on socket event. |
| this->_schedule_packet_write_ready(); |
| } |
| } |
| |
| SSL * |
| QUICNetVConnection::_get_ssl_object() const |
| { |
| return static_cast<QUICTLS *>(this->_hs_protocol)->ssl_handle(); |
| } |
| |
| ssl_curve_id |
| QUICNetVConnection::_get_tls_curve() const |
| { |
| if (this->getSSLSessionCacheHit()) { |
| return this->getSSLCurveNID(); |
| } else { |
| return SSLGetCurveNID(this->_get_ssl_object()); |
| } |
| } |
| |
| const IpEndpoint & |
| QUICNetVConnection::_getLocalEndpoint() |
| { |
| return local_addr; |
| } |