diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst index 2afcbf08530..d914af0595e 100644 --- a/doc/admin-guide/files/records.config.en.rst +++ b/doc/admin-guide/files/records.config.en.rst @@ -4092,13 +4092,13 @@ removed in the future without prior notice. This is just for debugging. Do not change it from the default value unless you really understand what this is. -.. ts:cv:: CONFIG proxy.config.quic.congestion_control.initial_window_scale INT 10 +.. ts:cv:: CONFIG proxy.config.quic.congestion_control.initial_window INT 12000 :reloadable: This is just for debugging. Do not change it from the default value unless you really understand what this is. -.. ts:cv:: CONFIG proxy.config.quic.congestion_control.minimum_window_scale INT 2 +.. ts:cv:: CONFIG proxy.config.quic.congestion_control.minimum_window INT 2400 :reloadable: This is just for debugging. Do not change it from the default value unless diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in index b2bf28140ce..57073351c10 100644 --- a/include/ts/apidefs.h.in +++ b/include/ts/apidefs.h.in @@ -1232,7 +1232,9 @@ extern tsapi const char *const TS_ALPN_PROTOCOL_HTTP_1_0; extern tsapi const char *const TS_ALPN_PROTOCOL_HTTP_1_1; extern tsapi const char *const TS_ALPN_PROTOCOL_HTTP_2_0; extern tsapi const char *const TS_ALPN_PROTOCOL_HTTP_3; +extern tsapi const char *const TS_ALPN_PROTOCOL_HTTP_3_D27; extern tsapi const char *const TS_ALPN_PROTOCOL_HTTP_QUIC; +extern tsapi const char *const TS_ALPN_PROTOCOL_HTTP_QUIC_D27; extern tsapi int TS_ALPN_PROTOCOL_INDEX_HTTP_0_9; extern tsapi int TS_ALPN_PROTOCOL_INDEX_HTTP_1_0; diff --git a/include/tscore/ink_inet.h b/include/tscore/ink_inet.h index aa6ddf057dc..c0a7cc11724 100644 --- a/include/tscore/ink_inet.h +++ b/include/tscore/ink_inet.h @@ -72,6 +72,8 @@ extern const std::string_view IP_PROTO_TAG_HTTP_1_1; extern const std::string_view IP_PROTO_TAG_HTTP_2_0; extern const std::string_view IP_PROTO_TAG_HTTP_QUIC; extern const std::string_view IP_PROTO_TAG_HTTP_3; +extern const std::string_view IP_PROTO_TAG_HTTP_QUIC_D27; +extern const std::string_view IP_PROTO_TAG_HTTP_3_D27; struct IpAddr; // forward declare. diff --git a/iocore/net/P_QUICNetVConnection.h b/iocore/net/P_QUICNetVConnection.h index 484ea456a88..f04f32da6c8 100644 --- a/iocore/net/P_QUICNetVConnection.h +++ b/iocore/net/P_QUICNetVConnection.h @@ -147,10 +147,11 @@ class QUICNetVConnection : public UnixNetVConnection, public: QUICNetVConnection(); ~QUICNetVConnection(); - void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *, + void init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *, QUICResetTokenTable *rtable); - void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, UDPConnection *, - QUICPacketHandler *, QUICResetTokenTable *rtable, QUICConnectionTable *ctable); + void init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, + QUICConnectionId retry_cid, UDPConnection *, QUICPacketHandler *, QUICResetTokenTable *rtable, + QUICConnectionTable *ctable); // accept new conn_id int acceptEvent(int event, Event *e); @@ -199,13 +200,20 @@ class QUICNetVConnection : public UnixNetVConnection, QUICConnectionId peer_connection_id() const override; QUICConnectionId original_connection_id() const override; QUICConnectionId first_connection_id() const override; + QUICConnectionId retry_source_connection_id() const override; + QUICConnectionId initial_source_connection_id() const override; QUICConnectionId connection_id() const override; std::string_view cids() const override; const QUICFiveTuple five_tuple() const override; uint32_t pmtu() const override; NetVConnectionContext_t direction() const override; + QUICVersion negotiated_version() const override; std::string_view negotiated_application_name() const override; bool is_closed() const override; + bool is_at_anti_amplification_limit() const override; + bool is_address_validation_completed() const override; + bool is_handshake_completed() const override; + bool has_keys_for(QUICPacketNumberSpace space) const override; // QUICConnection (QUICFrameHandler) std::vector interests() override; @@ -226,17 +234,20 @@ class QUICNetVConnection : public UnixNetVConnection, QUICConfig::scoped_config _quic_config; - QUICConnectionId _peer_quic_connection_id; // dst cid in local - QUICConnectionId _peer_old_quic_connection_id; // dst previous cid in local - QUICConnectionId _original_quic_connection_id; // dst cid of initial packet from client - QUICConnectionId _first_quic_connection_id; // dst cid of initial packet from client that doesn't have retry token - QUICConnectionId _quic_connection_id; // src cid in local + QUICConnectionId _peer_quic_connection_id; // dst cid in local + QUICConnectionId _peer_old_quic_connection_id; // dst previous cid in local + QUICConnectionId _original_quic_connection_id; // dst cid of initial packet from client + QUICConnectionId _first_quic_connection_id; // dst cid of initial packet from client that doesn't have retry token + QUICConnectionId _retry_source_connection_id; // src cid used for sending Retry packet + QUICConnectionId _initial_source_connection_id; // src cid used for Initial packet + QUICConnectionId _quic_connection_id; // src cid in local QUICFiveTuple _five_tuple; bool _connection_migration_initiated = false; char _cids_data[MAX_CIDS_SIZE] = {0}; std::string_view _cids; + QUICVersion _initial_version; UDPConnection *_udp_con = nullptr; QUICPacketProtectionKeyInfo _pp_key_info; QUICPacketHandler *_packet_handler = nullptr; @@ -277,6 +288,8 @@ class QUICNetVConnection : public UnixNetVConnection, uint32_t _state_closing_recv_packet_window = 1; uint64_t _flow_control_buffer_size = 1024; + void _init_submodules(); + void _schedule_packet_write_ready(bool delay = false); void _unschedule_packet_write_ready(); void _close_packet_write_ready(Event *data); @@ -304,9 +317,9 @@ class QUICNetVConnection : public UnixNetVConnection, uint64_t _maximum_stream_frame_data_size(); Ptr _store_frame(Ptr parent_block, size_t &size_added, uint64_t &max_frame_size, QUICFrame &frame, - std::vector &frames); + std::vector &frames); QUICPacketUPtr _packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel level, uint64_t max_packet_size, - std::vector &frames); + std::vector &frames); void _packetize_closing_frame(); QUICPacketUPtr _build_packet(uint8_t *packet_buf, QUICEncryptionLevel level, const Ptr &parent_block, bool retransmittable, bool probing, bool crypto); @@ -337,7 +350,9 @@ class QUICNetVConnection : public UnixNetVConnection, QUICPacketUPtr _dequeue_recv_packet(uint8_t *packet_buf, QUICPacketCreationResult &result); void _validate_new_path(const QUICPath &path); + bool _handshake_completed = false; int _complete_handshake_if_possible(); + void _switch_to_handshake_state(); void _switch_to_established_state(); void _switch_to_closing_state(QUICConnectionErrorUPtr error); diff --git a/iocore/net/P_QUICPacketHandler.h b/iocore/net/P_QUICPacketHandler.h index 0eaae2242ae..9428b89a4ba 100644 --- a/iocore/net/P_QUICPacketHandler.h +++ b/iocore/net/P_QUICPacketHandler.h @@ -87,7 +87,7 @@ class QUICPacketHandlerIn : public NetAccept, public QUICPacketHandler private: void _recv_packet(int event, UDPPacket *udp_packet) override; int _stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPConnection *connection, IpEndpoint from, QUICConnectionId dcid, - QUICConnectionId scid, QUICConnectionId *original_cid); + QUICConnectionId scid, QUICConnectionId *original_cid, QUICConnectionId *retry_cid, QUICVersion version); bool _send_stateless_reset(QUICConnectionId dcid, uint32_t instance_id, UDPConnection *udp_con, IpEndpoint &addr, size_t maximum_size); void _send_invalid_token_error(const uint8_t *initial_packet, uint64_t initial_packet_len, UDPConnection *connection, diff --git a/iocore/net/QUICNetProcessor.cc b/iocore/net/QUICNetProcessor.cc index 2075e81e0f2..5bdaa2b8dc7 100644 --- a/iocore/net/QUICNetProcessor.cc +++ b/iocore/net/QUICNetProcessor.cc @@ -150,7 +150,7 @@ QUICNetProcessor::connect_re(Continuation *cont, sockaddr const *remote_addr, Ne QUICConnectionId client_dst_cid; client_dst_cid.randomize(); // vc->init set handler of vc `QUICNetVConnection::startEvent` - vc->init(client_dst_cid, client_dst_cid, con, packet_handler, this->_rtable); + vc->init(QUIC_SUPPORTED_VERSIONS[0], client_dst_cid, client_dst_cid, con, packet_handler, this->_rtable); packet_handler->init(vc); // Connection ID will be changed diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc index f78186e9f49..332b0cbeee8 100644 --- a/iocore/net/QUICNetVConnection.cc +++ b/iocore/net/QUICNetVConnection.cc @@ -37,6 +37,8 @@ #include "QUICMultiCertConfigLoader.h" #include "QUICTLS.h" +#include "QUICNewRenoCongestionController.h" + #include "QUICStats.h" #include "QUICGlobals.h" #include "QUICDebugNames.h" @@ -57,6 +59,7 @@ static constexpr uint8_t QUANTUM_TEST_VALUE[1200] = {'Q'}; #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__); \ @@ -236,37 +239,44 @@ QUICNetVConnection::~QUICNetVConnection() // XXX This might be called on ET_UDP thread // Initialize QUICNetVC for out going connection (NET_VCONNECTION_OUT) void -QUICNetVConnection::init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *udp_con, +QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *udp_con, QUICPacketHandler *packet_handler, QUICResetTokenTable *rtable) { SET_HANDLER((NetVConnHandler)&QUICNetVConnection::startEvent); + 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(QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, - UDPConnection *udp_con, QUICPacketHandler *packet_handler, QUICResetTokenTable *rtable, - QUICConnectionTable *ctable) +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; @@ -280,6 +290,30 @@ QUICNetVConnection::init(QUICConnectionId peer_cid, QUICConnectionId original_ci 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(&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 @@ -379,17 +413,6 @@ void QUICNetVConnection::start() { ink_release_assert(this->thread != nullptr); - 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(&this->_rtt_measure, this, &this->_pp_key_info, this->_path_manager); 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 @@ -398,10 +421,10 @@ QUICNetVConnection::start() 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, this->_hs_protocol, this->_reset_token, this->_quic_config->stateless_retry()); + 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 { @@ -413,7 +436,7 @@ QUICNetVConnection::start() 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, this->_hs_protocol); + this->_handshake_handler = new QUICHandshake(this->_initial_version, this, this->_hs_protocol); 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()); @@ -426,12 +449,6 @@ QUICNetVConnection::start() this->_frame_dispatcher = new QUICFrameDispatcher(this); // Create frame handlers - this->_pinger = new QUICPinger(); - this->_padder = new QUICPadder(this->netvc_context); - this->_rtt_measure.init(this->_context->ld_config()); - this->_congestion_controller = new QUICNewRenoCongestionController(*_context); - this->_loss_detector = - new QUICLossDetector(*_context, this->_congestion_controller, &this->_rtt_measure, this->_pinger, this->_padder); this->_frame_dispatcher->add_handler(this->_loss_detector); this->_remote_flow_controller = new QUICRemoteConnectionFlowController(UINT64_MAX); @@ -596,6 +613,18 @@ 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 { @@ -671,6 +700,7 @@ void QUICNetVConnection::handle_received_packet(UDPPacket *packet) { this->_packet_recv_queue.enqueue(packet); + this->_loss_detector->on_datagram_received(); } void @@ -1077,6 +1107,44 @@ QUICNetVConnection::is_closed() const return this->handler == reinterpret_cast(&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 { @@ -1085,6 +1153,12 @@ QUICNetVConnection::_largest_acked_packet_number(QUICEncryptionLevel level) cons 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 { @@ -1114,6 +1188,7 @@ QUICNetVConnection::_state_handshake_process_packet(const QUICPacket &packet) error = this->_state_handshake_process_handshake_packet(static_cast(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; @@ -1208,6 +1283,8 @@ QUICNetVConnection::_state_handshake_process_initial_packet(const QUICInitialPac 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); } @@ -1246,6 +1323,7 @@ QUICNetVConnection::_state_handshake_process_retry_packet(const QUICRetryPacketR // discard all transport state this->_handshake_handler->reset(); + this->_handshake_handler->update(packet); this->_packet_factory.reset(); this->_loss_detector->reset(); @@ -1254,7 +1332,7 @@ QUICNetVConnection::_state_handshake_process_retry_packet(const QUICRetryPacketR // 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); + this->_hs_protocol->initialize_key_materials(this->_peer_quic_connection_id, packet.version()); // start handshake over this->_handshake_handler->do_handshake(); @@ -1447,8 +1525,8 @@ QUICNetVConnection::_state_common_send_packet() max_packet_size = std::min(max_packet_size, this->_verified_state.windows()); } - QUICPacketInfoUPtr packet_info = std::make_unique(); - QUICPacketUPtr packet = this->_packetize_frames(packet_buf, level, max_packet_size, packet_info->frames); + QUICSentPacketInfoUPtr packet_info = std::make_unique(); + QUICPacketUPtr packet = this->_packetize_frames(packet_buf, level, max_packet_size, packet_info->frames); if (packet) { // trigger callback @@ -1457,12 +1535,7 @@ QUICNetVConnection::_state_common_send_packet() packet_info->packet_number = packet->packet_number(); packet_info->time_sent = Thread::get_hrtime(); packet_info->ack_eliciting = packet->is_ack_eliciting(); - if (packet->type() == QUICPacketType::PROTECTED) { - packet_info->is_crypto_packet = false; - } else { - packet_info->is_crypto_packet = static_cast(*packet).is_crypto_packet(); - } - packet_info->in_flight = true; + packet_info->in_flight = true; if (packet_info->ack_eliciting) { packet_info->sent_bytes = packet->size(); } else { @@ -1488,12 +1561,13 @@ QUICNetVConnection::_state_common_send_packet() ink_assert(!"failed to protect buffer"); } - QUICConDebug("[TX] %s packet #%" PRIu64 " size=%zu", QUICDebugNames::packet_type(packet->type()), packet->packet_number(), - len); + 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; } @@ -1542,7 +1616,7 @@ QUICNetVConnection::_state_closing_send_packet() Ptr QUICNetVConnection::_store_frame(Ptr parent_block, size_t &size_added, uint64_t &max_frame_size, QUICFrame &frame, - std::vector &frames) + std::vector &frames) { Ptr new_block = frame.to_io_buffer_block(max_frame_size); @@ -1565,7 +1639,7 @@ QUICNetVConnection::_store_frame(Ptr parent_block, size_t &size_a if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { char msg[1024]; frame.debug_msg(msg, sizeof(msg)); - QUICConDebug("[TX] | %s", msg); + QUICConVDebug("[TX] | %s", msg); } frames.emplace_back(frame.id(), frame.generated_by()); @@ -1578,7 +1652,7 @@ QUICNetVConnection::_store_frame(Ptr parent_block, size_t &size_a QUICPacketUPtr QUICNetVConnection::_packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel level, uint64_t max_packet_size, - std::vector &frames) + std::vector &frames) { QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); if (max_packet_size <= MAX_PACKET_OVERHEAD) { @@ -1634,8 +1708,8 @@ QUICNetVConnection::_packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel l probing |= frame->is_probing_frame(); if (frame->is_flow_controlled()) { int ret = this->_remote_flow_controller->update(this->_stream_manager->total_offset_sent()); - QUICFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller->current_offset(), - this->_remote_flow_controller->current_limit()); + 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); @@ -1690,7 +1764,7 @@ QUICNetVConnection::_packetize_closing_frame() size_t size_added = 0; uint64_t max_frame_size = static_cast(max_size); - std::vector frames; + std::vector frames; Ptr first_block = make_ptr(new_IOBufferBlock()); Ptr last_block = first_block; first_block->alloc(iobuffer_size_to_index(0, BUFFER_SIZE_INDEX_32K)); @@ -1736,16 +1810,16 @@ QUICNetVConnection::_recv_and_ack(const QUICPacketR &packet, bool *has_non_probi if (is_flow_controlled) { int ret = this->_local_flow_controller->update(this->_stream_manager->total_offset_received()); - QUICFCDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller->current_offset(), - this->_local_flow_controller->current_limit()); + QUICFCVDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller->current_offset(), + this->_local_flow_controller->current_limit()); if (ret != 0) { return std::make_unique(QUICTransErrorCode::FLOW_CONTROL_ERROR); } this->_local_flow_controller->forward_limit(this->_stream_manager->total_reordered_bytes() + this->_flow_control_buffer_size); - QUICFCDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller->current_offset(), - this->_local_flow_controller->current_limit()); + 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); @@ -1890,11 +1964,11 @@ QUICNetVConnection::_dequeue_recv_packet(uint8_t *packet_buf, QUICPacketCreation switch (packet->type()) { case QUICPacketType::VERSION_NEGOTIATION: case QUICPacketType::RETRY: - QUICConDebug("[RX] %s packet size=%u", QUICDebugNames::packet_type(packet->type()), packet->size()); + QUICConVDebug("[RX] %s packet size=%u", QUICDebugNames::packet_type(packet->type()), packet->size()); break; default: - QUICConDebug("[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()); + 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; @@ -2022,11 +2096,14 @@ QUICNetVConnection::_complete_handshake_if_possible() this->_init_flow_control_params(this->_handshake_handler->local_transport_parameters(), this->_handshake_handler->remote_transport_parameters()); - // PN space doesn't matter but seems like this is the way to pick the LossDetector for 0-RTT and Short packet 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, @@ -2037,6 +2114,8 @@ QUICNetVConnection::_complete_handshake_if_possible() this->_start_application(); + this->_handshake_completed = true; + return 0; } diff --git a/iocore/net/QUICPacketHandler.cc b/iocore/net/QUICPacketHandler.cc index 7c9135130a6..4a69065ce3e 100644 --- a/iocore/net/QUICPacketHandler.cc +++ b/iocore/net/QUICPacketHandler.cc @@ -37,14 +37,17 @@ #include "QUICMultiCertConfigLoader.h" #include "QUICTLS.h" -static constexpr char debug_tag[] = "quic_sec"; +static constexpr char debug_tag[] = "quic_sec"; +static constexpr char v_debug_tag[] = "v_quic_sec"; #define QUICDebug(fmt, ...) Debug(debug_tag, fmt, ##__VA_ARGS__) -#define QUICDebugQC(qc, fmt, ...) Debug(debug_tag, "[%s] " fmt, qc->cids().data(), ##__VA_ARGS__) +#define QUICQCDebug(qc, fmt, ...) Debug(debug_tag, "[%s] " fmt, qc->cids().data(), ##__VA_ARGS__) // ["local dcid" - "local scid"] -#define QUICDebugDS(dcid, scid, fmt, ...) \ +#define QUICPHDebug(dcid, scid, fmt, ...) \ Debug(debug_tag, "[%08" PRIx32 "-%08" PRIx32 "] " fmt, dcid.h32(), scid.h32(), ##__VA_ARGS__) +#define QUICVPHDebug(dcid, scid, fmt, ...) \ + Debug(v_debug_tag, "[%08" PRIx32 "-%08" PRIx32 "] " fmt, dcid.h32(), scid.h32(), ##__VA_ARGS__) // // QUICPacketHandler @@ -99,7 +102,7 @@ QUICPacketHandler::_send_packet(UDPConnection *udp_con, IpEndpoint &addr, PtrgetPortNum(), buf_len); + QUICVPHDebug(dcid, scid, "send %s packet to %s from port %u size=%" PRId64, (QUICInvariants::is_long_header(buf) ? "LH" : "SH"), + ats_ip_nptop(&addr, ipb, sizeof(ipb)), udp_con->getPortNum(), buf_len); } udp_con->send(this->_get_continuation(), udp_packet); @@ -215,6 +218,7 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) IOBufferBlock *block = udp_packet->getIOBlockChain(); const uint8_t *buf = reinterpret_cast(block->buf()); uint64_t buf_len = block->size(); + QUICVersion version; if (buf_len == 0) { QUICDebug("Ignore packet - payload is too small"); @@ -238,25 +242,24 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) return; } - if (is_debug_tag_set(debug_tag)) { + if (is_debug_tag_set(v_debug_tag)) { ip_port_text_buffer ipb_from; ip_port_text_buffer ipb_to; - QUICDebugDS(scid, dcid, "recv LH packet from %s to %s size=%" PRId64, - ats_ip_nptop(&udp_packet->from.sa, ipb_from, sizeof(ipb_from)), - ats_ip_nptop(&udp_packet->to.sa, ipb_to, sizeof(ipb_to)), udp_packet->getPktLength()); + QUICVPHDebug(scid, dcid, "recv LH packet from %s to %s size=%" PRId64, + ats_ip_nptop(&udp_packet->from.sa, ipb_from, sizeof(ipb_from)), + ats_ip_nptop(&udp_packet->to.sa, ipb_to, sizeof(ipb_to)), udp_packet->getPktLength()); } - QUICVersion v; - if (unlikely(!QUICInvariants::version(v, buf, buf_len))) { + if (unlikely(!QUICInvariants::version(version, buf, buf_len))) { QUICDebug("Ignore packet - payload is too small"); udp_packet->free(); return; } - if (!QUICInvariants::is_version_negotiation(v) && !QUICTypeUtil::is_supported_version(v)) { - QUICDebugDS(scid, dcid, "Unsupported version: 0x%x", v); + if (!QUICInvariants::is_version_negotiation(version) && !QUICTypeUtil::is_supported_version(version)) { + QUICPHDebug(scid, dcid, "Unsupported version: 0x%x", version); - QUICPacketUPtr vn = QUICPacketFactory::create_version_negotiation_packet(scid, dcid); + QUICPacketUPtr vn = QUICPacketFactory::create_version_negotiation_packet(scid, dcid, version); this->_send_packet(*vn, udp_packet->getConnection(), udp_packet->from, 1200, nullptr, 0); udp_packet->free(); return; @@ -281,12 +284,12 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) } } else { // TODO: lookup DCID by 5-tuple when ATS omits SCID - if (is_debug_tag_set(debug_tag)) { + if (is_debug_tag_set(v_debug_tag)) { ip_port_text_buffer ipb_from; ip_port_text_buffer ipb_to; - QUICDebugDS(scid, dcid, "recv SH packet from %s to %s size=%" PRId64, - ats_ip_nptop(&udp_packet->from.sa, ipb_from, sizeof(ipb_from)), - ats_ip_nptop(&udp_packet->to.sa, ipb_to, sizeof(ipb_to)), udp_packet->getPktLength()); + QUICVPHDebug(scid, dcid, "recv SH packet from %s to %s size=%" PRId64, + ats_ip_nptop(&udp_packet->from.sa, ipb_from, sizeof(ipb_from)), + ats_ip_nptop(&udp_packet->to.sa, ipb_to, sizeof(ipb_to)), udp_packet->getPktLength()); } } @@ -295,9 +298,11 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) // Server Stateless Retry QUICConfig::scoped_config params; - QUICConnectionId cid_in_retry_token = QUICConnectionId::ZERO(); + QUICConnectionId ocid_in_retry_token = QUICConnectionId::ZERO(); + QUICConnectionId rcid_in_retry_token = QUICConnectionId::ZERO(); if (!vc && params->stateless_retry() && QUICInvariants::is_long_header(buf)) { - int ret = this->_stateless_retry(buf, buf_len, udp_packet->getConnection(), udp_packet->from, dcid, scid, &cid_in_retry_token); + int ret = this->_stateless_retry(buf, buf_len, udp_packet->getConnection(), udp_packet->from, dcid, scid, &ocid_in_retry_token, + &rcid_in_retry_token, version); if (ret < 0) { udp_packet->free(); return; @@ -320,7 +325,7 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) udp_packet->free(); if (is_debug_tag_set(debug_tag) && sent) { - QUICDebugDS(scid, dcid, "sent Stateless Reset : connection not found, dcid=%s", dcid.hex().c_str()); + QUICPHDebug(scid, dcid, "sent Stateless Reset : connection not found, dcid=%s", dcid.hex().c_str()); } return; @@ -331,7 +336,7 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) udp_packet->free(); if (is_debug_tag_set(debug_tag) && sent) { - QUICDebugDS(scid, dcid, "sent Stateless Reset : connection is already closed, dcid=%s", dcid.hex().c_str()); + QUICPHDebug(scid, dcid, "sent Stateless Reset : connection is already closed, dcid=%s", dcid.hex().c_str()); } return; @@ -348,11 +353,12 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) QUICConnectionId peer_cid = scid; if (is_debug_tag_set("quic_sec")) { - QUICDebugDS(peer_cid, original_cid, "client initial dcid=%s", original_cid.hex().c_str()); + QUICPHDebug(peer_cid, original_cid, "client initial dcid=%s", original_cid.hex().c_str()); } vc = static_cast(getNetProcessor()->allocate_vc(nullptr)); - vc->init(peer_cid, original_cid, cid_in_retry_token, udp_packet->getConnection(), this, &this->_rtable, &this->_ctable); + vc->init(version, peer_cid, original_cid, ocid_in_retry_token, rcid_in_retry_token, udp_packet->getConnection(), this, + &this->_rtable, &this->_ctable); vc->id = net_next_connection_number(); vc->con.move(con); vc->submit_time = Thread::get_hrtime(); @@ -393,7 +399,8 @@ QUICPacketHandler::send_packet(QUICNetVConnection *vc, const Ptr int QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPConnection *connection, IpEndpoint from, - QUICConnectionId dcid, QUICConnectionId scid, QUICConnectionId *original_cid) + QUICConnectionId dcid, QUICConnectionId scid, QUICConnectionId *original_cid, + QUICConnectionId *retry_cid, QUICVersion version) { QUICPacketType type = QUICPacketType::UNINITIALIZED; QUICPacketR::type(type, buf, buf_len); @@ -411,14 +418,14 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC } if (token_length == 0) { - QUICRetryToken token(from, dcid); QUICConnectionId local_cid; local_cid.randomize(); - QUICPacketUPtr retry_packet = QUICPacketFactory::create_retry_packet(scid, local_cid, token); + QUICRetryToken token(from, dcid, local_cid); + QUICPacketUPtr retry_packet = QUICPacketFactory::create_retry_packet(version, scid, local_cid, token); - QUICDebug("[TX] %s packet ODCID=%" PRIx64 " token_length=%u token=%02x%02x%02x%02x...", - QUICDebugNames::packet_type(retry_packet->type()), static_cast(token.original_dcid()), token.length(), - token.buf()[0], token.buf()[1], token.buf()[2], token.buf()[3]); + QUICDebug("[TX] %s packet ODCID=%" PRIx64 " RCID=%" PRIx64 " token_length=%u token=%02x%02x%02x%02x...", + QUICDebugNames::packet_type(retry_packet->type()), static_cast(token.original_dcid()), + static_cast(token.scid()), token.length(), token.buf()[0], token.buf()[1], token.buf()[2], token.buf()[3]); this->_send_packet(*retry_packet, connection, from, 1200, nullptr, 0); return -2; @@ -429,12 +436,14 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC QUICRetryToken token(buf + token_offset, token_length); if (token.is_valid(from)) { *original_cid = token.original_dcid(); - QUICDebug("Retry Token is valid. ODCID=%" PRIx64, static_cast(*original_cid)); + *retry_cid = token.scid(); + QUICDebug("Retry Token is valid. ODCID=%" PRIx64 " RCID=%" PRIx64, static_cast(*original_cid), + static_cast(*retry_cid)); return 0; } else { - QUICDebug("Retry token is invalid: ODCID=%" PRIx64 "token_length=%u token=%02x%02x%02x%02x...", - static_cast(token.original_dcid()), token.length(), token.buf()[0], token.buf()[1], token.buf()[2], - token.buf()[3]); + QUICDebug("Retry token is invalid: ODCID=%" PRIx64 " RCID=%" PRIx64 " token_length=%u token=%02x%02x%02x%02x...", + static_cast(token.original_dcid()), static_cast(*retry_cid), token.length(), token.buf()[0], + token.buf()[1], token.buf()[2], token.buf()[3]); this->_send_invalid_token_error(buf, buf_len, connection, from); return -3; } @@ -468,6 +477,8 @@ QUICPacketHandlerIn::_send_invalid_token_error(const uint8_t *initial_packet, ui QUICConnectionId dcid_in_initial; QUICInvariants::scid(scid_in_initial, initial_packet, initial_packet_len); QUICInvariants::dcid(dcid_in_initial, initial_packet, initial_packet_len); + QUICVersion version_in_initial; + QUICLongHeaderPacketR::version(version_in_initial, initial_packet, initial_packet_len); // Create CONNECTION_CLOSE frame auto error = std::make_unique(QUICTransErrorCode::INVALID_TOKEN); @@ -487,7 +498,7 @@ QUICPacketHandlerIn::_send_invalid_token_error(const uint8_t *initial_packet, ui QUICPacketHeaderProtector php(ppki); QUICCertConfig::scoped_config server_cert; QUICTLS tls(ppki, server_cert->ssl_default.get(), NET_VCONNECTION_IN, {}, "", ""); - tls.initialize_key_materials(dcid_in_initial); + tls.initialize_key_materials(dcid_in_initial, version_in_initial); // Create INITIAL packet QUICConnectionId scid; @@ -553,7 +564,7 @@ QUICPacketHandlerOut::_recv_packet(int event, UDPPacket *udp_packet) if (is_debug_tag_set(debug_tag)) { ip_port_text_buffer ipb_from; ip_port_text_buffer ipb_to; - QUICDebugQC(this->_vc, "recv %s packet from %s to %s size=%" PRId64, (QUICInvariants::is_long_header(buf) ? "LH" : "SH"), + QUICQCDebug(this->_vc, "recv %s packet from %s to %s size=%" PRId64, (QUICInvariants::is_long_header(buf) ? "LH" : "SH"), ats_ip_nptop(&udp_packet->from.sa, ipb_from, sizeof(ipb_from)), ats_ip_nptop(&udp_packet->to.sa, ipb_to, sizeof(ipb_to)), udp_packet->getPktLength()); } diff --git a/iocore/net/quic/Makefile.am b/iocore/net/quic/Makefile.am index 75c4dafa168..fc02e6d799f 100644 --- a/iocore/net/quic/Makefile.am +++ b/iocore/net/quic/Makefile.am @@ -142,7 +142,7 @@ TESTS = $(check_PROGRAMS) test_CPPFLAGS = \ $(AM_CPPFLAGS) \ - -I$(abs_top_srcdir)/tests/include + -I$(abs_top_srcdir)/tests/include -O0 test_LDADD = \ libquic.a \ diff --git a/iocore/net/quic/Mock.h b/iocore/net/quic/Mock.h index 68e421a9761..c0d28a3fa6d 100644 --- a/iocore/net/quic/Mock.h +++ b/iocore/net/quic/Mock.h @@ -28,6 +28,9 @@ #include "QUICApplication.h" #include "QUICStreamManager.h" #include "QUICLossDetector.h" +#include "QUICPacketProtectionKeyInfo.h" +#include "QUICPinger.h" +#include "QUICPadder.h" #include "QUICEvents.h" #include "QUICPacketProtectionKeyInfo.h" #include "QUICPinger.h" @@ -37,7 +40,7 @@ class MockQUICContext; using namespace std::literals; -std::string_view negotiated_application_name_sv = "h3-27"sv; +std::string_view negotiated_application_name_sv = "h3-29"sv; class MockQUICLDConfig : public QUICLDConfig { @@ -154,6 +157,18 @@ class MockQUICConnectionInfoProvider : public QUICConnectionInfoProvider return {reinterpret_cast("\x00"), 1}; } + QUICConnectionId + retry_source_connection_id() const override + { + return {reinterpret_cast("\x00"), 1}; + } + + QUICConnectionId + initial_source_connection_id() const override + { + return {reinterpret_cast("\x00"), 1}; + } + const QUICFiveTuple five_tuple() const override { @@ -192,6 +207,36 @@ class MockQUICConnectionInfoProvider : public QUICConnectionInfoProvider return false; } + bool + is_at_anti_amplification_limit() const override + { + return false; + } + + bool + is_address_validation_completed() const override + { + return true; + } + + bool + is_handshake_completed() const override + { + return true; + } + + bool + has_keys_for(QUICPacketNumberSpace space) const override + { + return true; + } + + QUICVersion + negotiated_version() const override + { + return QUIC_SUPPORTED_VERSIONS[0]; + } + std::string_view negotiated_application_name() const override { @@ -341,6 +386,18 @@ class MockQUICConnection : public QUICConnection return {reinterpret_cast("\x00"), 1}; } + QUICConnectionId + retry_source_connection_id() const override + { + return {reinterpret_cast("\x00"), 1}; + } + + QUICConnectionId + initial_source_connection_id() const override + { + return {reinterpret_cast("\x00"), 1}; + } + const QUICFiveTuple five_tuple() const override { @@ -409,6 +466,30 @@ class MockQUICConnection : public QUICConnection return false; } + bool + is_at_anti_amplification_limit() const override + { + return false; + } + + bool + is_address_validation_completed() const override + { + return true; + } + + bool + is_handshake_completed() const override + { + return true; + } + + bool + has_keys_for(QUICPacketNumberSpace space) const override + { + return true; + } + void handle_received_packet(UDPPacket *) override { @@ -419,6 +500,12 @@ class MockQUICConnection : public QUICConnection { } + QUICVersion + negotiated_version() const override + { + return QUIC_SUPPORTED_VERSIONS[0]; + } + std::string_view negotiated_application_name() const override { @@ -448,7 +535,7 @@ class MockQUICCongestionController : public QUICCongestionController MockQUICCongestionController() {} // Override virtual void - on_packets_lost(const std::map &packets) override + on_packets_lost(const std::map &packets) override { for (auto &p : packets) { lost_packets.insert(p.first); @@ -460,11 +547,15 @@ class MockQUICCongestionController : public QUICCongestionController { } virtual void - on_packet_acked(const QUICPacketInfo &acked_packet) override + on_packets_acked(const std::vector &packets) override + { + } + void + on_packet_number_space_discarded(size_t bytes_in_flight) override { } virtual void - process_ecn(const QUICPacketInfo &acked_largest_packet, const QUICAckFrame::EcnSection *ecn_section) override + process_ecn(const QUICAckFrame &ack_frame, QUICPacketNumberSpace pn_space, ink_hrtime largest_acked_time_sent) override { } virtual void @@ -627,6 +718,12 @@ class MockQUICLossDetector : public QUICLossDetector { } + void + on_packet_number_space_discarded(QUICPacketNumberSpace pn_space) + { + this->_cc.on_packet_number_space_discarded(0); + } + private: QUICPinger _pinger; QUICPadder _padder; @@ -740,7 +837,7 @@ class MockQUICHandshakeProtocol : public QUICHandshakeProtocol } int - initialize_key_materials(QUICConnectionId cid) override + initialize_key_materials(QUICConnectionId cid, QUICVersion version) override { return 0; } diff --git a/iocore/net/quic/QUICAckFrameCreator.cc b/iocore/net/quic/QUICAckFrameCreator.cc index dd2e2f963ce..bee2b76bc9d 100644 --- a/iocore/net/quic/QUICAckFrameCreator.cc +++ b/iocore/net/quic/QUICAckFrameCreator.cc @@ -28,7 +28,7 @@ QUICAckFrameManager::QUICAckFrameManager() { - for (auto i = 0; i < kPacketNumberSpace; i++) { + for (auto i = 0; i < QUIC_N_PACKET_SPACES; i++) { this->_ack_creator[i] = std::make_unique(static_cast(i), this); } } @@ -132,7 +132,7 @@ QUICAckFrameManager::ack_delay_exponent() const void QUICAckFrameManager::set_max_ack_delay(uint16_t delay) { - for (auto i = 0; i < kPacketNumberSpace; i++) { + for (auto i = 0; i < QUIC_N_PACKET_SPACES; i++) { this->_ack_creator[i]->set_max_ack_delay(delay); } } @@ -193,7 +193,7 @@ QUICAckFrameManager::QUICAckFrameCreator::push_back(QUICPacketNumber packet_numb } // can not delay handshake packet - if ((this->_pn_space == QUICPacketNumberSpace::Initial || this->_pn_space == QUICPacketNumberSpace::Handshake) && !ack_only) { + if ((this->_pn_space == QUICPacketNumberSpace::INITIAL || this->_pn_space == QUICPacketNumberSpace::HANDSHAKE) && !ack_only) { this->_should_send = true; } @@ -335,7 +335,7 @@ QUICAckFrameManager::QUICAckFrameCreator::_calculate_delay() ink_hrtime now = Thread::get_hrtime(); uint64_t delay = (now - this->_largest_ack_received_time) / 1000; uint8_t ack_delay_exponent = 3; - if (this->_pn_space != QUICPacketNumberSpace::Initial && this->_pn_space != QUICPacketNumberSpace::Handshake) { + if (this->_pn_space != QUICPacketNumberSpace::INITIAL && this->_pn_space != QUICPacketNumberSpace::HANDSHAKE) { ack_delay_exponent = this->_ack_manager->ack_delay_exponent(); } return delay >> ack_delay_exponent; diff --git a/iocore/net/quic/QUICAckFrameCreator.h b/iocore/net/quic/QUICAckFrameCreator.h index 4fc89ba7be5..55e20f254a9 100644 --- a/iocore/net/quic/QUICAckFrameCreator.h +++ b/iocore/net/quic/QUICAckFrameCreator.h @@ -78,7 +78,7 @@ class QUICAckFrameManager : public QUICFrameGenerator QUICAckFrameManager *_ack_manager = nullptr; - QUICPacketNumberSpace _pn_space = QUICPacketNumberSpace::Initial; + QUICPacketNumberSpace _pn_space = QUICPacketNumberSpace::INITIAL; }; static constexpr int MAXIMUM_PACKET_COUNT = 256; diff --git a/iocore/net/quic/QUICAddrVerifyState.cc b/iocore/net/quic/QUICAddrVerifyState.cc index 67281b592d6..6db501961c7 100644 --- a/iocore/net/quic/QUICAddrVerifyState.cc +++ b/iocore/net/quic/QUICAddrVerifyState.cc @@ -48,7 +48,7 @@ QUICAddrVerifyState::consume(uint32_t windows) } uint32_t -QUICAddrVerifyState::windows() +QUICAddrVerifyState::windows() const { return this->_windows; } diff --git a/iocore/net/quic/QUICAddrVerifyState.h b/iocore/net/quic/QUICAddrVerifyState.h index d6d6c7c4e42..6b454e4e22c 100644 --- a/iocore/net/quic/QUICAddrVerifyState.h +++ b/iocore/net/quic/QUICAddrVerifyState.h @@ -34,7 +34,7 @@ class QUICAddrVerifyState void fill(uint32_t windows); void consume(uint32_t windows); void set_addr_verifed(); - uint32_t windows(); + uint32_t windows() const; bool is_verified() const; private: diff --git a/iocore/net/quic/QUICBidirectionalStream.cc b/iocore/net/quic/QUICBidirectionalStream.cc index 9565b0ac444..a5d2f714ad1 100644 --- a/iocore/net/quic/QUICBidirectionalStream.cc +++ b/iocore/net/quic/QUICBidirectionalStream.cc @@ -469,8 +469,8 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, // Calling update always success, because len is always less than stream_credit ink_assert(ret == 0); - QUICStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), - this->_remote_flow_controller.current_limit()); + QUICVStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), + this->_remote_flow_controller.current_limit()); if (this->_remote_flow_controller.current_offset() == this->_remote_flow_controller.current_limit()) { QUICStreamDebug("Flow Controller will block sending a STREAM frame"); } diff --git a/iocore/net/quic/QUICConfig.cc b/iocore/net/quic/QUICConfig.cc index 8e39794d69a..aec4f137bc1 100644 --- a/iocore/net/quic/QUICConfig.cc +++ b/iocore/net/quic/QUICConfig.cc @@ -179,9 +179,8 @@ QUICConfigParams::initialize() this->_ld_initial_rtt = HRTIME_MSECONDS(timeout); // Congestion Control - REC_EstablishStaticConfigInt32U(this->_cc_max_datagram_size, "proxy.config.quic.congestion_control.max_datagram_size"); - REC_EstablishStaticConfigInt32U(this->_cc_initial_window_scale, "proxy.config.quic.congestion_control.initial_window_scale"); - REC_EstablishStaticConfigInt32U(this->_cc_minimum_window_scale, "proxy.config.quic.congestion_control.minimum_window_scale"); + REC_EstablishStaticConfigInt32U(this->_cc_initial_window, "proxy.config.quic.congestion_control.initial_window"); + REC_EstablishStaticConfigInt32U(this->_cc_minimum_window, "proxy.config.quic.congestion_control.minimum_window"); REC_EstablishStaticConfigFloat(this->_cc_loss_reduction_factor, "proxy.config.quic.congestion_control.loss_reduction_factor"); REC_EstablishStaticConfigInt32U(this->_cc_persistent_congestion_threshold, "proxy.config.quic.congestion_control.persistent_congestion_threshold"); @@ -419,27 +418,16 @@ QUICConfigParams::ld_initial_rtt() const return _ld_initial_rtt; } -uint32_t -QUICConfigParams::cc_max_datagram_size() const -{ - return _cc_max_datagram_size; -} - uint32_t QUICConfigParams::cc_initial_window() const { - // kInitialWindow: Default limit on the initial amount of data in - // flight, in bytes. Taken from [RFC6928]. The RECOMMENDED value is - // the minimum of 10 * kMaxDatagramSize and max(2* kMaxDatagramSize, - // 14600)). - return std::min(_cc_initial_window_scale * _cc_max_datagram_size, - std::max(2 * _cc_max_datagram_size, static_cast(14600))); + return _cc_initial_window; } uint32_t QUICConfigParams::cc_minimum_window() const { - return _cc_minimum_window_scale * _cc_max_datagram_size; + return _cc_minimum_window; } float diff --git a/iocore/net/quic/QUICConfig.h b/iocore/net/quic/QUICConfig.h index 38a655a8c14..0ce045a97de 100644 --- a/iocore/net/quic/QUICConfig.h +++ b/iocore/net/quic/QUICConfig.h @@ -146,9 +146,8 @@ class QUICConfigParams : public ConfigInfo ink_hrtime _ld_initial_rtt = HRTIME_MSECONDS(500); // [draft-11 recovery] 4.7.1. Constants of interest - uint32_t _cc_max_datagram_size = 1200; - uint32_t _cc_initial_window_scale = 10; // Actual initial window size is this value multiplied by the _cc_default_mss - uint32_t _cc_minimum_window_scale = 2; // Actual minimum window size is this value multiplied by the _cc_default_mss + uint32_t _cc_initial_window = 1200 * 10; + uint32_t _cc_minimum_window = 1200 * 2; float _cc_loss_reduction_factor = 0.5; uint32_t _cc_persistent_congestion_threshold = 3; }; diff --git a/iocore/net/quic/QUICCongestionController.h b/iocore/net/quic/QUICCongestionController.h index 42b4a8bf415..7417ac873ad 100644 --- a/iocore/net/quic/QUICCongestionController.h +++ b/iocore/net/quic/QUICCongestionController.h @@ -25,22 +25,6 @@ #include "QUICFrame.h" -struct QUICPacketInfo { - // 6.3.1. Sent Packet Fields - QUICPacketNumber packet_number; - ink_hrtime time_sent; - bool ack_eliciting; - bool is_crypto_packet; - bool in_flight; - size_t sent_bytes; - - // addition - QUICPacketType type; - std::vector frames; - QUICPacketNumberSpace pn_space; - // end -}; - class QUICCongestionController { public: @@ -52,15 +36,21 @@ class QUICCongestionController }; virtual ~QUICCongestionController() {} - virtual void on_packet_sent(size_t bytes_sent) = 0; - virtual void on_packet_acked(const QUICPacketInfo &acked_packet) = 0; - virtual void process_ecn(const QUICPacketInfo &acked_largest_packet, const QUICAckFrame::EcnSection *ecn_section) = 0; - virtual void on_packets_lost(const std::map &packets) = 0; - virtual void add_extra_credit() = 0; - virtual void reset() = 0; - virtual uint32_t credit() const = 0; + // Appendix B. Congestion Control Pseudocode + virtual void on_packet_sent(size_t bytes_sent) = 0; + virtual void on_packets_acked(const std::vector &packets) = 0; + virtual void process_ecn(const QUICAckFrame &ack, QUICPacketNumberSpace pn_space, ink_hrtime largest_acked_packet_time_sent) = 0; + virtual void on_packets_lost(const std::map &packets) = 0; + // The function signature is different from the pseudo code because LD takes care of most of the processes + virtual void on_packet_number_space_discarded(size_t bytes_in_flight) = 0; + + // These are additional and not on the spec + virtual void add_extra_credit() = 0; + virtual void reset() = 0; + virtual uint32_t credit() const = 0; + virtual uint32_t bytes_in_flight() const = 0; + // Debug - virtual uint32_t bytes_in_flight() const = 0; virtual uint32_t congestion_window() const = 0; virtual uint32_t current_ssthresh() const = 0; }; diff --git a/iocore/net/quic/QUICConnection.h b/iocore/net/quic/QUICConnection.h index 57d64ba2807..02cbbb04db9 100644 --- a/iocore/net/quic/QUICConnection.h +++ b/iocore/net/quic/QUICConnection.h @@ -37,16 +37,32 @@ class QUICConnectionInfoProvider virtual ~QUICConnectionInfoProvider() {} virtual QUICConnectionId peer_connection_id() const = 0; virtual QUICConnectionId original_connection_id() const = 0; - virtual QUICConnectionId first_connection_id() const = 0; - virtual QUICConnectionId connection_id() const = 0; - virtual std::string_view cids() const = 0; - virtual const QUICFiveTuple five_tuple() const = 0; + /** + * This is S1 on 7.3.Authenticating Connection IDs + */ + virtual QUICConnectionId first_connection_id() const = 0; + /** + * This is S2 on 7.3.Authenticating Connection IDs + */ + virtual QUICConnectionId retry_source_connection_id() const = 0; + /** + * This is C1 or S3 on 7.3.Authenticating Connection IDs + */ + virtual QUICConnectionId initial_source_connection_id() const = 0; + virtual QUICConnectionId connection_id() const = 0; + virtual std::string_view cids() const = 0; + virtual const QUICFiveTuple five_tuple() const = 0; virtual uint32_t pmtu() const = 0; virtual NetVConnectionContext_t direction() const = 0; virtual int select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned inlen) const = 0; virtual bool is_closed() const = 0; + virtual bool is_at_anti_amplification_limit() const = 0; + virtual bool is_address_validation_completed() const = 0; + virtual bool is_handshake_completed() const = 0; + virtual bool has_keys_for(QUICPacketNumberSpace space) const = 0; + virtual QUICVersion negotiated_version() const = 0; virtual std::string_view negotiated_application_name() const = 0; }; diff --git a/iocore/net/quic/QUICContext.cc b/iocore/net/quic/QUICContext.cc index 4d0d5019317..b065f56102a 100644 --- a/iocore/net/quic/QUICContext.cc +++ b/iocore/net/quic/QUICContext.cc @@ -32,12 +32,6 @@ class QUICCCConfigQCP : public QUICCCConfig virtual ~QUICCCConfigQCP() {} QUICCCConfigQCP(const QUICConfigParams *params) : _params(params) {} - uint32_t - max_datagram_size() const override - { - return this->_params->cc_max_datagram_size(); - } - uint32_t initial_window() const override { @@ -151,4 +145,4 @@ QUICPathManager * QUICContext::path_manager() const { return _path_manager; -} \ No newline at end of file +} diff --git a/iocore/net/quic/QUICContext.h b/iocore/net/quic/QUICContext.h index 13f8935c7b3..ab2dae9a694 100644 --- a/iocore/net/quic/QUICContext.h +++ b/iocore/net/quic/QUICContext.h @@ -23,6 +23,7 @@ #pragma once +#include "QUICTypes.h" #include "QUICConnection.h" #include "QUICConfig.h" #include "QUICEvents.h" @@ -54,7 +55,7 @@ class QUICCallback // callback on packet send event virtual void packet_send_callback(QUICCallbackContext &, const QUICPacket &p){}; // callback on packet lost event - virtual void packet_lost_callback(QUICCallbackContext &, const QUICPacketInfo &p){}; + virtual void packet_lost_callback(QUICCallbackContext &, const QUICSentPacketInfo &p){}; // callback on packet receive event virtual void packet_recv_callback(QUICCallbackContext &, const QUICPacket &p){}; // callback on packet acked event @@ -128,7 +129,7 @@ class QUICContext } void - trigger(CallbackEvent e, const QUICPacketInfo &p) + trigger(CallbackEvent e, const QUICSentPacketInfo &p) { QUICCallbackContext ctx; for (auto &&it : this->_callbacks) { diff --git a/iocore/net/quic/QUICDebugNames.cc b/iocore/net/quic/QUICDebugNames.cc index 6a75032cd18..79d2797f688 100644 --- a/iocore/net/quic/QUICDebugNames.cc +++ b/iocore/net/quic/QUICDebugNames.cc @@ -121,6 +121,8 @@ QUICDebugNames::error_code(uint16_t code) return "NO_ERROR"; case static_cast(QUICTransErrorCode::INTERNAL_ERROR): return "INTERNAL_ERROR"; + case static_cast(QUICTransErrorCode::CONNECTION_REFUSED): + return "CONNECTION_REFUSED"; case static_cast(QUICTransErrorCode::FLOW_CONTROL_ERROR): return "FLOW_CONTROL_ERROR"; case static_cast(QUICTransErrorCode::STREAM_LIMIT_ERROR): @@ -139,6 +141,8 @@ QUICDebugNames::error_code(uint16_t code) return "PROTOCOL_VIOLATION"; case static_cast(QUICTransErrorCode::INVALID_TOKEN): return "INVALID_TOKEN"; + case static_cast(QUICTransErrorCode::APPLICATION_ERROR): + return "APPLICATION_ERROR"; case static_cast(QUICTransErrorCode::CRYPTO_BUFFER_EXCEEDED): return "CRYPTO_BUFFER_EXCEEDED"; default: @@ -189,8 +193,8 @@ QUICDebugNames::transport_parameter_id(QUICTransportParameterId id) return "MAX_IDLE_TIMEOUT"; case QUICTransportParameterId::PREFERRED_ADDRESS: return "PREFERRED_ADDRESS"; - case QUICTransportParameterId::MAX_PACKET_SIZE: - return "MAX_PACKET_SIZE"; + case QUICTransportParameterId::MAX_UDP_PAYLOAD_SIZE: + return "MAX_UDP_PAYLOAD_SIZE"; case QUICTransportParameterId::STATELESS_RESET_TOKEN: return "STATELESS_RESET_TOKEN"; case QUICTransportParameterId::ACK_DELAY_EXPONENT: @@ -205,10 +209,14 @@ QUICDebugNames::transport_parameter_id(QUICTransportParameterId id) return "INITIAL_MAX_STREAM_DATA_UNI"; case QUICTransportParameterId::MAX_ACK_DELAY: return "INITIAL_MAX_ACK_DELAY"; - case QUICTransportParameterId::ORIGINAL_CONNECTION_ID: - return "INITIAL_ORIGINAL_CONNECTION_ID"; + case QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID: + return "INITIAL_ORIGINAL_DESTINATION_CONNECTION_ID"; case QUICTransportParameterId::ACTIVE_CONNECTION_ID_LIMIT: return "ACTIVE_CONNECTION_ID_LIMIT"; + case QUICTransportParameterId::INITIAL_SOURCE_CONNECTION_ID: + return "INITIAL_SOURCE_CONNECTION_ID"; + case QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID: + return "RETRY_SOURCE_CONNECTION_ID"; default: return "UNKNOWN"; } @@ -323,12 +331,12 @@ const char * QUICDebugNames::pn_space(QUICPacketNumberSpace pn_space) { switch (pn_space) { - case QUICPacketNumberSpace::Initial: - return "QUICPacketNumberSpace::Initial"; - case QUICPacketNumberSpace::Handshake: - return "QUICPacketNumberSpace::Handshake"; - case QUICPacketNumberSpace::ApplicationData: - return "QUICPacketNumberSpace::ApplicationData"; + case QUICPacketNumberSpace::INITIAL: + return "INITIAL"; + case QUICPacketNumberSpace::HANDSHAKE: + return "HANDSHAKE"; + case QUICPacketNumberSpace::APPLICATION_DATA: + return "APPLCIATION_DATA"; default: return "UNKNOWN"; } diff --git a/iocore/net/quic/QUICFrame.cc b/iocore/net/quic/QUICFrame.cc index 7af9a7a11b5..35c14b68390 100644 --- a/iocore/net/quic/QUICFrame.cc +++ b/iocore/net/quic/QUICFrame.cc @@ -3104,15 +3104,3 @@ QUICFrameFactory::create_handshake_done_frame(uint8_t *buf, QUICFrameId id, QUIC new (buf) QUICHandshakeDoneFrame(id, owner); return reinterpret_cast(buf); } - -QUICFrameId -QUICFrameInfo::id() const -{ - return this->_id; -} - -QUICFrameGenerator * -QUICFrameInfo::generated_by() const -{ - return this->_generator; -} diff --git a/iocore/net/quic/QUICFrame.h b/iocore/net/quic/QUICFrame.h index 14b87d37a9e..93cd692fc7c 100644 --- a/iocore/net/quic/QUICFrame.h +++ b/iocore/net/quic/QUICFrame.h @@ -40,8 +40,6 @@ class QUICCryptoFrame; class QUICPacketR; class QUICFrameGenerator; -using QUICFrameId = uint64_t; - class QUICFrame { public: @@ -908,15 +906,3 @@ class QUICFrameFactory uint8_t _buf_for_fast_create[256 * QUICFrame::MAX_INSTANCE_SIZE]; QUICUnknownFrame _unknown_frame; }; - -class QUICFrameInfo -{ -public: - QUICFrameInfo(QUICFrameId id, QUICFrameGenerator *generator) : _id(id), _generator(generator) {} - QUICFrameId id() const; - QUICFrameGenerator *generated_by() const; - -private: - QUICFrameId _id = 0; - QUICFrameGenerator *_generator; -}; diff --git a/iocore/net/quic/QUICFrameDispatcher.cc b/iocore/net/quic/QUICFrameDispatcher.cc index 95099332bc1..131bc4001ae 100644 --- a/iocore/net/quic/QUICFrameDispatcher.cc +++ b/iocore/net/quic/QUICFrameDispatcher.cc @@ -24,9 +24,11 @@ #include "QUICFrameDispatcher.h" #include "QUICDebugNames.h" -static constexpr char tag[] = "quic_net"; +static constexpr char tag[] = "quic_net"; +static constexpr char v_tag[] = "v_quic_net"; #define QUICDebug(fmt, ...) Debug(tag, "[%s] " fmt, this->_info->cids().data(), ##__VA_ARGS__) +#define QUICVDebug(fmt, ...) Debug(v_tag, "[%s] " fmt, this->_info->cids().data(), ##__VA_ARGS__) // // Frame Dispatcher @@ -70,10 +72,10 @@ QUICFrameDispatcher::receive_frames(QUICContext &ctx, QUICEncryptionLevel level, is_flow_controlled = true; } - if (is_debug_tag_set(tag) && type != QUICFrameType::PADDING) { + if (is_debug_tag_set(v_tag) && type != QUICFrameType::PADDING) { char msg[1024]; frame.debug_msg(msg, sizeof(msg)); - QUICDebug("[RX] | %s", msg); + QUICVDebug("[RX] | %s", msg); } if (type != QUICFrameType::PADDING && type != QUICFrameType::ACK) { diff --git a/iocore/net/quic/QUICHandshake.cc b/iocore/net/quic/QUICHandshake.cc index c1a530fdc85..f64f2221315 100644 --- a/iocore/net/quic/QUICHandshake.cc +++ b/iocore/net/quic/QUICHandshake.cc @@ -82,16 +82,20 @@ static constexpr int UDP_MAXIMUM_PAYLOAD_SIZE = 65527; -QUICHandshake::QUICHandshake(QUICConnection *qc, QUICHandshakeProtocol *hsp) : QUICHandshake(qc, hsp, {}, false) {} +QUICHandshake::QUICHandshake(QUICVersion version, QUICConnection *qc, QUICHandshakeProtocol *hsp) + : QUICHandshake(version, qc, hsp, {}, false) +{ +} -QUICHandshake::QUICHandshake(QUICConnection *qc, QUICHandshakeProtocol *hsp, QUICStatelessResetToken token, bool stateless_retry) +QUICHandshake::QUICHandshake(QUICVersion version, QUICConnection *qc, QUICHandshakeProtocol *hsp, QUICStatelessResetToken token, + bool stateless_retry) : _qc(qc), _hs_protocol(hsp), _version_negotiator(new QUICVersionNegotiator()), _reset_token(token), _stateless_retry(stateless_retry) { - this->_hs_protocol->initialize_key_materials(this->_qc->original_connection_id()); + this->_hs_protocol->initialize_key_materials(this->_qc->original_connection_id(), version); if (this->_qc->direction() == NET_VCONNECTION_OUT) { this->_client_initial = true; @@ -108,7 +112,7 @@ QUICHandshake::start(const QUICTPConfig &tp_config, QUICPacketFactory *packet_fa { QUICVersion initial_version = QUIC_SUPPORTED_VERSIONS[0]; if (vn_exercise_enabled) { - initial_version = QUIC_EXERCISE_VERSION; + initial_version = QUIC_EXERCISE_VERSION1; } this->_load_local_client_transport_parameters(tp_config); @@ -137,6 +141,7 @@ QUICHandshake::start(const QUICTPConfig &tp_config, const QUICInitialPacketR &in } else { return std::make_unique(QUICTransErrorCode::PROTOCOL_VIOLATION); } + this->_initial_source_cid_received = initial_packet.source_cid(); } return nullptr; } @@ -253,6 +258,17 @@ QUICHandshake::_check_remote_transport_parameters(std::shared_ptrnegotiated_version() == QUIC_SUPPORTED_VERSIONS[0]) { // draft-28 + uint16_t cid_buf_len; + const uint8_t *cid_buf = tp->getAsBytes(QUICTransportParameterId::INITIAL_SOURCE_CONNECTION_ID, cid_buf_len); + QUICConnectionId cid_in_tp(cid_buf, cid_buf_len); + if (cid_in_tp != this->_initial_source_cid_received) { + this->_abort_handshake(QUICTransErrorCode::PROTOCOL_VIOLATION); + return false; + } + } + this->_remote_transport_parameters = tp; return true; @@ -268,6 +284,26 @@ QUICHandshake::_check_remote_transport_parameters(std::shared_ptrnegotiated_version() == QUIC_SUPPORTED_VERSIONS[0]) { // draft-28 + uint16_t cid_buf_len; + const uint8_t *cid_buf = tp->getAsBytes(QUICTransportParameterId::INITIAL_SOURCE_CONNECTION_ID, cid_buf_len); + QUICConnectionId cid_in_tp(cid_buf, cid_buf_len); + if (cid_in_tp != this->_initial_source_cid_received) { + this->_abort_handshake(QUICTransErrorCode::PROTOCOL_VIOLATION); + return false; + } + + if (!this->_retry_source_cid_received.is_zero()) { + cid_buf = tp->getAsBytes(QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID, cid_buf_len); + QUICConnectionId cid_in_tp(cid_buf, cid_buf_len); + if (cid_in_tp != this->_retry_source_cid_received) { + this->_abort_handshake(QUICTransErrorCode::PROTOCOL_VIOLATION); + return false; + } + } + } + this->_remote_transport_parameters = tp; return true; @@ -302,6 +338,18 @@ QUICHandshake::reset() } } +void +QUICHandshake::update(const QUICInitialPacketR &packet) +{ + this->_initial_source_cid_received = packet.source_cid(); +} + +void +QUICHandshake::update(const QUICRetryPacketR &packet) +{ + this->_retry_source_cid_received = packet.source_cid(); +} + std::vector QUICHandshake::interests() { @@ -386,8 +434,19 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co // MUSTs tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); if (this->_stateless_retry) { - tp->set(QUICTransportParameterId::ORIGINAL_CONNECTION_ID, this->_qc->first_connection_id(), + tp->set(QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID, this->_qc->first_connection_id(), this->_qc->first_connection_id().length()); + tp->set(QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID, this->_qc->retry_source_connection_id(), + this->_qc->retry_source_connection_id().length()); + } else { + if (this->negotiated_version() == QUIC_SUPPORTED_VERSIONS[0]) { // draft-28 + tp->set(QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID, this->_qc->original_connection_id(), + this->_qc->original_connection_id().length()); + } + } + if (this->negotiated_version() == QUIC_SUPPORTED_VERSIONS[0]) { // draft-28 + tp->set(QUICTransportParameterId::INITIAL_SOURCE_CONNECTION_ID, this->_qc->initial_source_connection_id(), + this->_qc->initial_source_connection_id().length()); } // MAYs @@ -426,7 +485,10 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co tp->set(QUICTransportParameterId::STATELESS_RESET_TOKEN, this->_reset_token.buf(), QUICStatelessResetToken::LEN); tp->set(QUICTransportParameterId::ACK_DELAY_EXPONENT, tp_config.ack_delay_exponent()); - tp->add_version(QUIC_SUPPORTED_VERSIONS[0]); + // Additional parameters + for (auto &¶m : tp_config.additional_tp()) { + tp->set(param.first, param.second.first, param.second.second); + } // Additional parameters for (auto &¶m : tp_config.additional_tp()) { @@ -444,6 +506,8 @@ QUICHandshake::_load_local_client_transport_parameters(const QUICTPConfig &tp_co // MUSTs tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); + tp->set(QUICTransportParameterId::INITIAL_SOURCE_CONNECTION_ID, this->_qc->initial_source_connection_id(), + this->_qc->initial_source_connection_id().length()); // MAYs if (tp_config.initial_max_data() != 0) { diff --git a/iocore/net/quic/QUICHandshake.h b/iocore/net/quic/QUICHandshake.h index 017c96d5a07..4f55985badf 100644 --- a/iocore/net/quic/QUICHandshake.h +++ b/iocore/net/quic/QUICHandshake.h @@ -42,9 +42,10 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator { public: // Constructor for client side - QUICHandshake(QUICConnection *qc, QUICHandshakeProtocol *hsp); + QUICHandshake(QUICVersion version, QUICConnection *qc, QUICHandshakeProtocol *hsp); // Constructor for server side - QUICHandshake(QUICConnection *qc, QUICHandshakeProtocol *hsp, QUICStatelessResetToken token, bool stateless_retry); + QUICHandshake(QUICVersion version, QUICConnection *qc, QUICHandshakeProtocol *hsp, QUICStatelessResetToken token, + bool stateless_retry); ~QUICHandshake(); // QUICFrameHandler @@ -60,6 +61,8 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, QUICPacketFactory *packet_factory, bool vn_exercise_enabled); QUICConnectionErrorUPtr negotiate_version(const QUICVersionNegotiationPacketR &packet, QUICPacketFactory *packet_factory); void reset(); + void update(const QUICInitialPacketR &initial_packet); + void update(const QUICRetryPacketR &retry_packet); // for server side QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, const QUICInitialPacketR &initial_packet, @@ -91,8 +94,10 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator QUICVersionNegotiator *_version_negotiator = nullptr; QUICStatelessResetToken _reset_token; - bool _client_initial = false; - bool _stateless_retry = false; + bool _client_initial = false; + bool _stateless_retry = false; + QUICConnectionId _initial_source_cid_received = QUICConnectionId::ZERO(); + QUICConnectionId _retry_source_cid_received = QUICConnectionId::ZERO(); QUICCryptoStream _crypto_streams[4]; diff --git a/iocore/net/quic/QUICHandshakeProtocol.h b/iocore/net/quic/QUICHandshakeProtocol.h index 0c7c09b55e7..8bc6e776186 100644 --- a/iocore/net/quic/QUICHandshakeProtocol.h +++ b/iocore/net/quic/QUICHandshakeProtocol.h @@ -47,7 +47,7 @@ class QUICHandshakeProtocol virtual void reset() = 0; virtual bool is_handshake_finished() const = 0; virtual bool is_ready_to_derive() const = 0; - virtual int initialize_key_materials(QUICConnectionId cid) = 0; + virtual int initialize_key_materials(QUICConnectionId cid, QUICVersion) = 0; virtual const char *negotiated_cipher_suite() const = 0; virtual void negotiated_application_name(const uint8_t **name, unsigned int *len) const = 0; diff --git a/iocore/net/quic/QUICKeyGenerator.cc b/iocore/net/quic/QUICKeyGenerator.cc index 24c2b84628b..7ac32baf653 100644 --- a/iocore/net/quic/QUICKeyGenerator.cc +++ b/iocore/net/quic/QUICKeyGenerator.cc @@ -34,6 +34,9 @@ using namespace std::literals; constexpr static uint8_t QUIC_VERSION_1_SALT[] = { + 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99, +}; +constexpr static uint8_t QUIC_VERSION_1_D27_SALT[] = { 0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02, }; constexpr static std::string_view LABEL_FOR_CLIENT_INITIAL_SECRET("client in"sv); @@ -43,7 +46,7 @@ constexpr static std::string_view LABEL_FOR_IV("quic iv"sv); constexpr static std::string_view LABEL_FOR_HP("quic hp"sv); void -QUICKeyGenerator::generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICConnectionId cid) +QUICKeyGenerator::generate(QUICVersion version, uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICConnectionId cid) { const EVP_CIPHER *cipher = this->_get_cipher_for_initial(); const EVP_MD *md = EVP_sha256(); @@ -53,7 +56,7 @@ QUICKeyGenerator::generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t switch (this->_ctx) { case Context::CLIENT: - this->_generate_initial_secret(secret, &secret_len, hkdf, cid, LABEL_FOR_CLIENT_INITIAL_SECRET.data(), + this->_generate_initial_secret(version, secret, &secret_len, hkdf, cid, LABEL_FOR_CLIENT_INITIAL_SECRET.data(), LABEL_FOR_CLIENT_INITIAL_SECRET.length(), EVP_MD_size(md)); if (is_debug_tag_set("vv_quic_crypto")) { uint8_t print_buf[1024 + 1]; @@ -63,7 +66,7 @@ QUICKeyGenerator::generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t break; case Context::SERVER: - this->_generate_initial_secret(secret, &secret_len, hkdf, cid, LABEL_FOR_SERVER_INITIAL_SECRET.data(), + this->_generate_initial_secret(version, secret, &secret_len, hkdf, cid, LABEL_FOR_SERVER_INITIAL_SECRET.data(), LABEL_FOR_SERVER_INITIAL_SECRET.length(), EVP_MD_size(md)); if (is_debug_tag_set("vv_quic_crypto")) { uint8_t print_buf[1024 + 1]; @@ -101,18 +104,33 @@ QUICKeyGenerator::_generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_ } int -QUICKeyGenerator::_generate_initial_secret(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, QUICConnectionId cid, const char *label, - size_t label_len, size_t length) +QUICKeyGenerator::_generate_initial_secret(QUICVersion version, uint8_t *out, size_t *out_len, QUICHKDF &hkdf, QUICConnectionId cid, + const char *label, size_t label_len, size_t length) { uint8_t client_connection_id[QUICConnectionId::MAX_LENGTH]; size_t cid_len = 0; uint8_t initial_secret[512]; size_t initial_secret_len = sizeof(initial_secret); + const uint8_t *salt; + size_t salt_len; // TODO: do not extract initial secret twice QUICTypeUtil::write_QUICConnectionId(cid, client_connection_id, &cid_len); - if (hkdf.extract(initial_secret, &initial_secret_len, QUIC_VERSION_1_SALT, sizeof(QUIC_VERSION_1_SALT), client_connection_id, - cid.length()) != 1) { + switch (version) { + case 0xff00001d: // Draft-29 + salt = QUIC_VERSION_1_SALT; + salt_len = sizeof(QUIC_VERSION_1_SALT); + break; + case 0xff00001b: // Draft-27 + salt = QUIC_VERSION_1_D27_SALT; + salt_len = sizeof(QUIC_VERSION_1_D27_SALT); + break; + default: + salt = QUIC_VERSION_1_SALT; + salt_len = sizeof(QUIC_VERSION_1_SALT); + break; + } + if (hkdf.extract(initial_secret, &initial_secret_len, salt, salt_len, client_connection_id, cid.length()) != 1) { return -1; } diff --git a/iocore/net/quic/QUICKeyGenerator.h b/iocore/net/quic/QUICKeyGenerator.h index c2ff89f78fa..db2924c7864 100644 --- a/iocore/net/quic/QUICKeyGenerator.h +++ b/iocore/net/quic/QUICKeyGenerator.h @@ -43,7 +43,7 @@ class QUICKeyGenerator * Generate keys for Initial encryption level * The keys for the remaining encryption level are derived by TLS stack with "quic " prefix */ - void generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICConnectionId cid); + void generate(QUICVersion version, uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICConnectionId cid); void regenerate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, const uint8_t *secret, size_t secret_len, const EVP_CIPHER *cipher, QUICHKDF &hkdf); @@ -56,8 +56,8 @@ class QUICKeyGenerator int _generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICHKDF &hkdf, const uint8_t *secret, size_t secret_len, const EVP_CIPHER *cipher); - int _generate_initial_secret(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, QUICConnectionId cid, const char *label, - size_t label_len, size_t length); + int _generate_initial_secret(QUICVersion version, uint8_t *out, size_t *out_len, QUICHKDF &hkdf, QUICConnectionId cid, + const char *label, size_t label_len, size_t length); int _generate_key(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t key_length) const; int _generate_iv(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t iv_length) const; diff --git a/iocore/net/quic/QUICLossDetector.cc b/iocore/net/quic/QUICLossDetector.cc index 3d381b25cb8..a52ec684d42 100644 --- a/iocore/net/quic/QUICLossDetector.cc +++ b/iocore/net/quic/QUICLossDetector.cc @@ -61,7 +61,7 @@ QUICLossDetector::~QUICLossDetector() this->_loss_detection_timer = nullptr; } - for (auto i = 0; i < kPacketNumberSpace; i++) { + for (auto i = 0; i < QUIC_N_PACKET_SPACES; i++) { this->_sent_packets[i].clear(); } } @@ -125,60 +125,91 @@ QUICLossDetector::largest_acked_packet_number(QUICPacketNumberSpace pn_space) co } void -QUICLossDetector::on_packet_sent(QUICPacketInfoUPtr packet_info, bool in_flight) +QUICLossDetector::on_packet_sent(QUICSentPacketInfoUPtr packet_info, bool in_flight) { + // ADDITIONAL CODE if (packet_info->type == QUICPacketType::VERSION_NEGOTIATION) { return; } + // END OF ADDITIONAL CODE SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); QUICPacketNumber packet_number = packet_info->packet_number; bool ack_eliciting = packet_info->ack_eliciting; - bool is_crypto_packet = packet_info->is_crypto_packet; ink_hrtime now = packet_info->time_sent; size_t sent_bytes = packet_info->sent_bytes; + QUICPacketNumberSpace pn_space = packet_info->pn_space; - QUICLDDebug("%s packet sent : %" PRIu64 " bytes: %lu ack_eliciting: %d", QUICDebugNames::pn_space(packet_info->pn_space), - packet_number, sent_bytes, ack_eliciting); + QUICLDVDebug("%s packet sent : %" PRIu64 " bytes: %lu ack_eliciting: %d", QUICDebugNames::pn_space(packet_info->pn_space), + packet_number, sent_bytes, ack_eliciting); this->_add_to_sent_packet_list(packet_number, std::move(packet_info)); if (in_flight) { - if (is_crypto_packet) { - this->_time_of_last_sent_crypto_packet = now; - } - if (ack_eliciting) { - this->_time_of_last_sent_ack_eliciting_packet = now; + this->_time_of_last_ack_eliciting_packet[static_cast(pn_space)] = now; } this->_cc->on_packet_sent(sent_bytes); this->_set_loss_detection_timer(); } } +void +QUICLossDetector::on_datagram_received() +{ + if (this->_context.connection_info()->is_at_anti_amplification_limit()) { + this->_set_loss_detection_timer(); + } +} + +void +QUICLossDetector::on_packet_number_space_discarded(QUICPacketNumberSpace pn_space) +{ + ink_assert(pn_space != QUICPacketNumberSpace::APPLICATION_DATA); + size_t bytes_in_flight = 0; + for (auto it = this->_sent_packets[static_cast(pn_space)].begin(); + it != this->_sent_packets[static_cast(pn_space)].end();) { + auto ret = this->_remove_from_sent_packet_list(it, pn_space); + auto &pi = ret.first; + if (pi->in_flight) { + bytes_in_flight += pi->sent_bytes; + } + it = ret.second; + } + this->_cc->on_packet_number_space_discarded(bytes_in_flight); + // Reset the loss detection and PTO timer + this->_time_of_last_ack_eliciting_packet[static_cast(pn_space)] = 0; + this->_loss_time[static_cast(pn_space)] = 0; + this->_rtt_measure->set_pto_count(0); + this->_set_loss_detection_timer(); + QUICLDDebug("[%s] Packets have been discarded because keys for the space are discarded", QUICDebugNames::pn_space(pn_space)); +} + void QUICLossDetector::reset() { SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); + + // A.4. Initialization if (this->_loss_detection_timer) { this->_loss_detection_timer->cancel(); this->_loss_detection_timer = nullptr; } - - this->_ack_eliciting_outstanding = 0; - this->_crypto_outstanding = 0; - - // [draft-17 recovery] 6.4.3. Initialization - this->_time_of_last_sent_ack_eliciting_packet = 0; - this->_time_of_last_sent_crypto_packet = 0; - for (auto i = 0; i < kPacketNumberSpace; i++) { - this->_largest_acked_packet[i] = 0; - this->_loss_time[i] = 0; + this->_rtt_measure->reset(); + for (auto i = 0; i < QUIC_N_PACKET_SPACES; i++) { + this->_largest_acked_packet[i] = UINT64_MAX; + this->_time_of_last_ack_eliciting_packet[i] = 0; + this->_loss_time[i] = 0; this->_sent_packets[i].clear(); + // ADDITIONAL CODE + this->_num_packets_in_flight[i] = 0; + // END OF ADDITIONAL CODE } - this->_rtt_measure->reset(); + // ADDITIONAL CODE + this->_ack_eliciting_outstanding = 0; + // END OF ADDITIONAL CODE } void @@ -188,12 +219,12 @@ QUICLossDetector::update_ack_delay_exponent(uint8_t ack_delay_exponent) } bool -QUICLossDetector::_include_ack_eliciting(const std::vector &acked_packets, int index) const +QUICLossDetector::_include_ack_eliciting(const std::vector &acked_packets) const { // Find out ack_elicting packet. // FIXME: this loop is the same as _on_ack_received's loop it would better // to combine it. - for (auto packet : acked_packets) { + for (const auto &packet : acked_packets) { if (packet->ack_eliciting) { return true; } @@ -207,65 +238,68 @@ QUICLossDetector::_on_ack_received(const QUICAckFrame &ack_frame, QUICPacketNumb { SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); - int index = static_cast(pn_space); - this->_largest_acked_packet[index] = std::max(this->_largest_acked_packet[index], ack_frame.largest_acknowledged()); + int index = static_cast(pn_space); + if (this->_largest_acked_packet[index] == UINT64_MAX) { + this->_largest_acked_packet[index] = ack_frame.largest_acknowledged(); + } else { + this->_largest_acked_packet[index] = std::max(this->_largest_acked_packet[index], ack_frame.largest_acknowledged()); + } - auto newly_acked_packets = this->_determine_newly_acked_packets(ack_frame, index); + auto newly_acked_packets = this->_detect_and_remove_acked_packets(ack_frame, pn_space); if (newly_acked_packets.empty()) { return; } // If the largest acknowledged is newly acked and // ack-eliciting, update the RTT. - auto pi = this->_sent_packets[index].find(ack_frame.largest_acknowledged()); - if (pi != this->_sent_packets[index].end() && - (pi->second->ack_eliciting || this->_include_ack_eliciting(newly_acked_packets, index))) { - ink_hrtime latest_rtt = Thread::get_hrtime() - pi->second->time_sent; + const auto &largest_acked = newly_acked_packets[0]; + if (largest_acked->packet_number == ack_frame.largest_acknowledged() && this->_include_ack_eliciting(newly_acked_packets)) { + ink_hrtime latest_rtt = Thread::get_hrtime() - largest_acked->time_sent; // _latest_rtt is nanosecond but ack_frame.ack_delay is microsecond and scaled - ink_hrtime delay = HRTIME_USECONDS(ack_frame.ack_delay() << this->_ack_delay_exponent); - this->_rtt_measure->update_rtt(latest_rtt, delay); + ink_hrtime ack_delay = 0; + if (pn_space == QUICPacketNumberSpace::APPLICATION_DATA) { + ack_delay = HRTIME_USECONDS(ack_frame.ack_delay() << this->_ack_delay_exponent); + } + this->_rtt_measure->update_rtt(latest_rtt, ack_delay); } - QUICLDVDebug("[%s] Unacked packets %lu (retransmittable %u, includes %u handshake packets)", QUICDebugNames::pn_space(pn_space), - this->_sent_packets[index].size(), this->_ack_eliciting_outstanding.load(), this->_crypto_outstanding.load()); - // if (ACK frame contains ECN information): // ProcessECN(ack) - if (ack_frame.ecn_section() != nullptr && pi != this->_sent_packets[index].end()) { - this->_cc->process_ecn(*pi->second, ack_frame.ecn_section()); + if (ack_frame.ecn_section() != nullptr) { + this->_cc->process_ecn(ack_frame, pn_space, largest_acked->time_sent); } + // ADDITIONAL CODE // Find all newly acked packets. - for (auto info : newly_acked_packets) { + for (const auto &info : newly_acked_packets) { this->_on_packet_acked(*info); } + // END OF ADDITIONAL CODE - QUICLDVDebug("[%s] Unacked packets %lu (retransmittable %u, includes %u handshake packets)", QUICDebugNames::pn_space(pn_space), - this->_sent_packets[index].size(), this->_ack_eliciting_outstanding.load(), this->_crypto_outstanding.load()); - - this->_detect_lost_packets(pn_space); - - this->_rtt_measure->set_crypto_count(0); - this->_rtt_measure->set_pto_count(0); + auto lost_packets = this->_detect_and_remove_lost_packets(pn_space); + if (!lost_packets.empty()) { + this->_cc->on_packets_lost(lost_packets); + } + this->_cc->on_packets_acked(newly_acked_packets); - QUICLDDebug("[%s] Unacked packets %lu (retransmittable %u, includes %u handshake packets)", QUICDebugNames::pn_space(pn_space), - this->_sent_packets[index].size(), this->_ack_eliciting_outstanding.load(), this->_crypto_outstanding.load()); + QUICLDVDebug("[%s] Newly acked:%lu Lost:%lu Unacked packets:%lu (%u ack eliciting)", QUICDebugNames::pn_space(pn_space), + newly_acked_packets.size(), lost_packets.size(), this->_sent_packets[index].size(), + this->_ack_eliciting_outstanding.load()); + if (this->_peer_completed_address_validation()) { + this->_rtt_measure->set_pto_count(0); + } this->_set_loss_detection_timer(); } void -QUICLossDetector::_on_packet_acked(const QUICPacketInfo &acked_packet) +QUICLossDetector::_on_packet_acked(const QUICSentPacketInfo &acked_packet) { SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); - QUICLDDebug("[%s] Packet number %" PRIu64 " has been acked", QUICDebugNames::pn_space(acked_packet.pn_space), - acked_packet.packet_number); - - if (acked_packet.in_flight) { - this->_cc->on_packet_acked(acked_packet); - } + QUICLDVDebug("[%s] Packet number %" PRIu64 " has been acked", QUICDebugNames::pn_space(acked_packet.pn_space), + acked_packet.packet_number); - for (const QUICFrameInfo &frame_info : acked_packet.frames) { + for (const QUICSentPacketInfo::FrameInfo &frame_info : acked_packet.frames) { auto reactor = frame_info.generated_by(); if (reactor == nullptr) { continue; @@ -273,17 +307,15 @@ QUICLossDetector::_on_packet_acked(const QUICPacketInfo &acked_packet) reactor->on_frame_acked(frame_info.id()); } - - this->_remove_from_sent_packet_list(acked_packet.packet_number, acked_packet.pn_space); } ink_hrtime -QUICLossDetector::_get_earliest_loss_time(QUICPacketNumberSpace &pn_space) +QUICLossDetector::_get_loss_time_and_space(QUICPacketNumberSpace &pn_space) { - ink_hrtime time = this->_loss_time[static_cast(QUICPacketNumberSpace::Initial)]; - pn_space = QUICPacketNumberSpace::Initial; - for (auto i = 1; i < kPacketNumberSpace; i++) { - if (this->_loss_time[i] != 0 && (time != 0 || this->_loss_time[i] < time)) { + ink_hrtime time = this->_loss_time[static_cast(QUICPacketNumberSpace::INITIAL)]; + pn_space = QUICPacketNumberSpace::INITIAL; + for (auto i = 1; i < QUIC_N_PACKET_SPACES; i++) { + if (time == 0 || this->_loss_time[i] < time) { time = this->_loss_time[i]; pn_space = static_cast(i); } @@ -292,6 +324,59 @@ QUICLossDetector::_get_earliest_loss_time(QUICPacketNumberSpace &pn_space) return time; } +ink_hrtime +QUICLossDetector::_get_pto_time_and_space(QUICPacketNumberSpace &space) +{ + ink_hrtime duration = + (this->_rtt_measure->smoothed_rtt() + std::max(4 * this->_rtt_measure->rttvar(), this->_rtt_measure->k_granularity())) * + (1 << this->_rtt_measure->pto_count()); + + // Arm PTO from now when there are no inflight packets. + if (this->_num_packets_in_flight[static_cast(QUICPacketNumberSpace::INITIAL)].load() == 0 && + this->_num_packets_in_flight[static_cast(QUICPacketNumberSpace::HANDSHAKE)].load() == 0 && + this->_num_packets_in_flight[static_cast(QUICPacketNumberSpace::APPLICATION_DATA)].load() == 0) { + ink_assert(!this->_peer_completed_address_validation()); + if (this->_context.connection_info()->has_keys_for(QUICPacketNumberSpace::HANDSHAKE)) { + space = QUICPacketNumberSpace::HANDSHAKE; + return Thread::get_hrtime() + duration; + } else { + space = QUICPacketNumberSpace::INITIAL; + return Thread::get_hrtime() + duration; + } + } + ink_hrtime pto_timeout = INT64_MAX; + QUICPacketNumberSpace pto_space = QUICPacketNumberSpace::INITIAL; + for (int i = 0; i < QUIC_N_PACKET_SPACES; ++i) { + if (this->_num_packets_in_flight[i].load() == 0) { + continue; + } + if (i == static_cast(QUICPacketNumberSpace::APPLICATION_DATA)) { + // Skip ApplicationData until handshake complete. + if (!this->_context.connection_info()->is_address_validation_completed()) { + space = pto_space; + return pto_timeout; + } + // Include max_ack_delay and backoff for ApplicationData. + // FIXME should be set by transport parameters + duration += this->_rtt_measure->max_ack_delay() * (1 << this->_rtt_measure->pto_count()); + } + + ink_hrtime t = this->_time_of_last_ack_eliciting_packet[i] + duration; + if (t < pto_timeout) { + pto_timeout = t; + pto_space = QUICPacketNumberSpace(i); + } + } + space = pto_space; + return pto_timeout; +} + +bool +QUICLossDetector::_peer_completed_address_validation() const +{ + return this->_context.connection_info()->is_address_validation_completed(); +} + void QUICLossDetector::_set_loss_detection_timer() { @@ -302,112 +387,115 @@ QUICLossDetector::_set_loss_detection_timer() } }; + std::function cancel_timer = [this]() { + this->_loss_detection_alarm_at = 0; + this->_loss_detection_timer->cancel(); + this->_loss_detection_timer = nullptr; + }; + QUICPacketNumberSpace pn_space; - ink_hrtime alarm = this->_get_earliest_loss_time(pn_space); - if (alarm != 0) { - update_timer(alarm); + ink_hrtime earliest_loss_time = this->_get_loss_time_and_space(pn_space); + if (earliest_loss_time != 0) { + update_timer(earliest_loss_time); QUICLDDebug("[%s] time threshold loss detection timer: %" PRId64 "ms", QUICDebugNames::pn_space(pn_space), (this->_loss_detection_alarm_at - Thread::get_hrtime()) / HRTIME_MSECOND); return; } - if (this->_crypto_outstanding > 0 || this->_is_client_without_one_rtt_key()) { - // Crypto retransmission timer. - alarm = this->_time_of_last_sent_crypto_packet + this->_rtt_measure->handshake_retransmit_timeout(); - update_timer(alarm); - QUICLDDebug("%s crypto packet alarm will be set: %" PRId64 "ms", QUICDebugNames::pn_space(pn_space), - (alarm - this->_time_of_last_sent_crypto_packet) / HRTIME_MSECOND); - return; + if (this->_context.connection_info()->is_at_anti_amplification_limit()) { + if (this->_loss_detection_timer) { + cancel_timer(); + QUICLDDebug("Loss detection alarm has been unset because of anti-amplification limit"); + return; + } } // Don't arm the alarm if there are no packets with retransmittable data in flight. - // -- MODIFIED CODE -- - // In pseudocode, `bytes_in_flight` is used, but we're tracking "retransmittable data in flight" by `_ack_eliciting_outstanding` - if (this->_ack_eliciting_outstanding == 0) { + if (this->_ack_eliciting_outstanding == 0 && this->_peer_completed_address_validation()) { if (this->_loss_detection_timer) { - this->_loss_detection_alarm_at = 0; - this->_loss_detection_timer->cancel(); - this->_loss_detection_timer = nullptr; - QUICLDDebug("Loss detection alarm has been unset"); + cancel_timer(); + QUICLDDebug("Loss detection alarm has been unset because of no ack eliciting packets outstanding"); } - return; } - // -- END OF MODIFIED CODE -- // PTO Duration - alarm = this->_time_of_last_sent_ack_eliciting_packet + this->_rtt_measure->current_pto_period(); - update_timer(alarm); - QUICLDDebug("[%s] PTO timeout will be set: %" PRId64 "ms", QUICDebugNames::pn_space(pn_space), - (alarm - this->_time_of_last_sent_ack_eliciting_packet) / HRTIME_MSECOND); + ink_hrtime timeout = this->_get_pto_time_and_space(pn_space); + update_timer(timeout); + QUICLDVDebug("[%s] PTO timeout has been set: %" PRId64 "ms", QUICDebugNames::pn_space(pn_space), + (timeout - this->_time_of_last_ack_eliciting_packet[static_cast(pn_space)]) / HRTIME_MSECOND); } void QUICLossDetector::_on_loss_detection_timeout() { QUICPacketNumberSpace pn_space; - ink_hrtime loss_time = this->_get_earliest_loss_time(pn_space); - if (loss_time != 0) { + ink_hrtime earliest_loss_time = this->_get_loss_time_and_space(pn_space); + if (earliest_loss_time != 0) { // Time threshold loss Detection - this->_detect_lost_packets(pn_space); - } else if (this->_crypto_outstanding) { - // Handshake retransmission alarm. - QUICLDVDebug("Crypto Retranmission"); - this->_retransmit_all_unacked_crypto_data(); - this->_rtt_measure->set_crypto_count(this->_rtt_measure->crypto_count() + 1); - } else if (this->_is_client_without_one_rtt_key()) { + auto lost_packets = this->_detect_and_remove_lost_packets(pn_space); + ink_assert(!lost_packets.empty()); + this->_cc->on_packets_lost(lost_packets); + this->_set_loss_detection_timer(); + return; + } + + if (this->_cc->bytes_in_flight() > 0) { + // PTO. Send new data if available, else retransmit old data. + // If neither is available, send a single PING frame. + QUICPacketNumberSpace pns; + this->_get_pto_time_and_space(pns); + this->_send_one_or_two_ack_eliciting_packet(pns); + } else { + ink_assert(this->_is_client_without_one_rtt_key()); // Client sends an anti-deadlock packet: Initial is padded // to earn more anti-amplification credit, // a Handshake packet proves address ownership. if (this->_context.key_info()->is_encryption_key_available(QUICKeyPhase::HANDSHAKE)) { - this->_send_one_handshake_packets(); + this->_send_one_ack_eliciting_handshake_packet(); } else { - this->_send_one_padded_packets(); + this->_send_one_ack_eliciting_padded_initial_packet(); } - - this->_rtt_measure->set_crypto_count(this->_rtt_measure->crypto_count() + 1); - } else { - QUICLDVDebug("PTO"); - this->_send_one_or_two_packet(); - this->_rtt_measure->set_pto_count(this->_rtt_measure->pto_count() + 1); } - QUICLDDebug("[%s] Unacked packets %lu (retransmittable %u, includes %u handshake packets)", QUICDebugNames::pn_space(pn_space), - this->_sent_packets[static_cast(pn_space)].size(), this->_ack_eliciting_outstanding.load(), - this->_crypto_outstanding.load()); + this->_rtt_measure->set_pto_count(this->_rtt_measure->pto_count() + 1); + this->_set_loss_detection_timer(); + + QUICLDDebug("[%s] Unacked packets %lu (ack_eliciting %u)", QUICDebugNames::pn_space(pn_space), + this->_sent_packets[static_cast(pn_space)].size(), this->_ack_eliciting_outstanding.load()); if (is_debug_tag_set("v_quic_loss_detector")) { for (auto i = 0; i < 3; i++) { for (auto &unacked : this->_sent_packets[i]) { - QUICLDVDebug("[%s] #%" PRIu64 " is_crypto=%i ack_eliciting=%i size=%zu %u %u", - QUICDebugNames::pn_space(static_cast(i)), unacked.first, - unacked.second->is_crypto_packet, unacked.second->ack_eliciting, unacked.second->sent_bytes, - this->_ack_eliciting_outstanding.load(), this->_crypto_outstanding.load()); + QUICLDVDebug("[%s] #%" PRIu64 " ack_eliciting=%i size=%zu %u", + QUICDebugNames::pn_space(static_cast(i)), unacked.first, unacked.second->ack_eliciting, + unacked.second->sent_bytes, this->_ack_eliciting_outstanding.load()); } } } - - this->_set_loss_detection_timer(); } -void -QUICLossDetector::_detect_lost_packets(QUICPacketNumberSpace pn_space) +std::map +QUICLossDetector::_detect_and_remove_lost_packets(QUICPacketNumberSpace pn_space) { SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); + ink_assert(this->_largest_acked_packet[static_cast(pn_space)] != UINT64_MAX); + this->_loss_time[static_cast(pn_space)] = 0; + std::map lost_packets; ink_hrtime loss_delay = this->_k_time_threshold * std::max(this->_rtt_measure->latest_rtt(), this->_rtt_measure->smoothed_rtt()); - loss_delay = std::min(loss_delay, this->_rtt_measure->k_granularity()); - std::map lost_packets; + // Minimum time of kGranularity before packets are deemed lost. + loss_delay = std::max(loss_delay, this->_rtt_measure->k_granularity()); // Packets sent before this time are deemed lost. ink_hrtime lost_send_time = Thread::get_hrtime() - loss_delay; // Packets with packet numbers before this are deemed lost. - QUICPacketNumber lost_pn = this->_largest_acked_packet[static_cast(pn_space)] - this->_k_packet_threshold; + // QUICPacketNumber lost_pn = this->_largest_acked_packet[static_cast(pn_space)] - this->_k_packet_threshold; for (auto it = this->_sent_packets[static_cast(pn_space)].begin(); - it != this->_sent_packets[static_cast(pn_space)].end(); ++it) { + it != this->_sent_packets[static_cast(pn_space)].end();) { if (it->first > this->_largest_acked_packet[static_cast(pn_space)]) { // the spec uses continue but we can break here because the _sent_packets is sorted by packet_number. break; @@ -416,8 +504,9 @@ QUICLossDetector::_detect_lost_packets(QUICPacketNumberSpace pn_space) auto &unacked = it->second; // Mark packet as lost, or set time when it should be marked. - if (unacked->time_sent < lost_send_time || unacked->packet_number < lost_pn) { - if (unacked->time_sent < lost_send_time) { + if (unacked->time_sent <= lost_send_time || + this->_largest_acked_packet[static_cast(pn_space)] >= unacked->packet_number + this->_k_packet_threshold) { + if (unacked->time_sent <= lost_send_time) { QUICLDDebug("[%s] Lost: time since sent is too long (#%" PRId64 " sent=%" PRId64 ", delay=%" PRId64 ", fraction=%lf, lrtt=%" PRId64 ", srtt=%" PRId64 ")", QUICDebugNames::pn_space(pn_space), it->first, unacked->time_sent, lost_send_time, this->_k_time_threshold, @@ -428,60 +517,41 @@ QUICLossDetector::_detect_lost_packets(QUICPacketNumberSpace pn_space) this->_k_packet_threshold); } - if (unacked->in_flight) { - lost_packets.insert({it->first, it->second.get()}); + auto ret = this->_remove_from_sent_packet_list(it, pn_space); + auto pi = std::move(ret.first); + it = ret.second; + if (pi->in_flight) { + this->_context.trigger(QUICContext::CallbackEvent::PACKET_LOST, *pi); + lost_packets.emplace(pi->packet_number, std::move(pi)); } - } else if (this->_loss_time[static_cast(pn_space)] == 0) { - this->_loss_time[static_cast(pn_space)] = unacked->time_sent + loss_delay; + } else { - this->_loss_time[static_cast(pn_space)] = - std::min(this->_loss_time[static_cast(pn_space)], unacked->time_sent + loss_delay); + if (this->_loss_time[static_cast(pn_space)] == 0) { + this->_loss_time[static_cast(pn_space)] = unacked->time_sent + loss_delay; + } else { + this->_loss_time[static_cast(pn_space)] = + std::min(this->_loss_time[static_cast(pn_space)], unacked->time_sent + loss_delay); + } + ++it; } } - // Inform the congestion controller of lost packets and - // lets it decide whether to retransmit immediately. + // -- ADDITIONAL CODE -- + // Not sure how we can get feedback from congestion control and when we should retransmit the lost packets but we need to send + // them somewhere. + // I couldn't find the place so just send them here for now. if (!lost_packets.empty()) { - this->_cc->on_packets_lost(lost_packets); - for (auto lost_packet : lost_packets) { - this->_context.trigger(QUICContext::CallbackEvent::PACKET_LOST, *lost_packet.second); - // -- ADDITIONAL CODE -- - // Not sure how we can get feedback from congestion control and when we should retransmit the lost packets but we need to send - // them somewhere. - // I couldn't find the place so just send them here for now. + for (const auto &lost_packet : lost_packets) { this->_retransmit_lost_packet(*lost_packet.second); - // -- END OF ADDITIONAL CODE -- - // -- ADDITIONAL CODE -- - this->_remove_from_sent_packet_list(lost_packet.first, pn_space); - // -- END OF ADDITIONAL CODE -- } } + // -- END OF ADDITIONAL CODE -- + + return lost_packets; } // ===== Functions below are used on the spec but there're no pseudo code ===== -void -QUICLossDetector::_retransmit_all_unacked_crypto_data() -{ - SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); - for (auto i = 0; i < kPacketNumberSpace; i++) { - std::set retransmitted_crypto_packets; - std::map lost_packets; - for (auto &info : this->_sent_packets[i]) { - if (info.second->is_crypto_packet) { - retransmitted_crypto_packets.insert(info.first); - this->_retransmit_lost_packet(*info.second); - lost_packets.insert({info.first, info.second.get()}); - } - } - - this->_cc->on_packets_lost(lost_packets); - for (auto packet_number : retransmitted_crypto_packets) { - this->_remove_from_sent_packet_list(packet_number, static_cast(i)); - } - } -} - void QUICLossDetector::_send_packet(QUICEncryptionLevel level, bool padded) { @@ -494,7 +564,7 @@ QUICLossDetector::_send_packet(QUICEncryptionLevel level, bool padded) } void -QUICLossDetector::_send_one_or_two_packet() +QUICLossDetector::_send_one_or_two_ack_eliciting_packet(QUICPacketNumberSpace pn_space) { this->_send_packet(QUICEncryptionLevel::ONE_RTT); this->_send_packet(QUICEncryptionLevel::ONE_RTT); @@ -504,7 +574,7 @@ QUICLossDetector::_send_one_or_two_packet() } void -QUICLossDetector::_send_one_handshake_packets() +QUICLossDetector::_send_one_ack_eliciting_handshake_packet() { this->_send_packet(QUICEncryptionLevel::HANDSHAKE); QUICLDDebug("[%s] send handshake packet: ping count=%" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::HANDSHAKE), @@ -512,7 +582,7 @@ QUICLossDetector::_send_one_handshake_packets() } void -QUICLossDetector::_send_one_padded_packets() +QUICLossDetector::_send_one_ack_eliciting_padded_initial_packet() { this->_send_packet(QUICEncryptionLevel::INITIAL, true); QUICLDDebug("[%s] send PADDING frame: ping count=%" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::INITIAL), @@ -522,12 +592,12 @@ QUICLossDetector::_send_one_padded_packets() // ===== Functions below are helper functions ===== void -QUICLossDetector::_retransmit_lost_packet(const QUICPacketInfo &packet_info) +QUICLossDetector::_retransmit_lost_packet(const QUICSentPacketInfo &packet_info) { SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); QUICLDDebug("Retransmit %s packet #%" PRIu64, QUICDebugNames::packet_type(packet_info.type), packet_info.packet_number); - for (const QUICFrameInfo &frame_info : packet_info.frames) { + for (const QUICSentPacketInfo::FrameInfo &frame_info : packet_info.frames) { auto reactor = frame_info.generated_by(); if (reactor == nullptr) { continue; @@ -537,11 +607,13 @@ QUICLossDetector::_retransmit_lost_packet(const QUICPacketInfo &packet_info) } } -std::vector -QUICLossDetector::_determine_newly_acked_packets(const QUICAckFrame &ack_frame, int pn_space) +std::vector +QUICLossDetector::_detect_and_remove_acked_packets(const QUICAckFrame &ack_frame, QUICPacketNumberSpace pn_space) { - std::vector packets; + std::vector packets; std::set numbers; + int index = static_cast(pn_space); + QUICPacketNumber x = ack_frame.largest_acknowledged(); numbers.insert({x, static_cast(x) - ack_frame.ack_block_section()->first_ack_block()}); x -= ack_frame.ack_block_section()->first_ack_block() + 1; @@ -552,9 +624,13 @@ QUICLossDetector::_determine_newly_acked_packets(const QUICAckFrame &ack_frame, } for (auto &&range : numbers) { - for (auto ite = this->_sent_packets[pn_space].rbegin(); ite != this->_sent_packets[pn_space].rend(); ite++) { + for (auto ite = this->_sent_packets[index].begin(); ite != this->_sent_packets[index].end();) { if (range.contains(ite->first)) { - packets.push_back(ite->second.get()); + auto ret = this->_remove_from_sent_packet_list(ite, pn_space); + packets.push_back(std::move(ret.first)); + ite = ret.second; + } else { + ++ite; } } } @@ -563,62 +639,48 @@ QUICLossDetector::_determine_newly_acked_packets(const QUICAckFrame &ack_frame, } void -QUICLossDetector::_add_to_sent_packet_list(QUICPacketNumber packet_number, QUICPacketInfoUPtr packet_info) +QUICLossDetector::_add_to_sent_packet_list(QUICPacketNumber packet_number, QUICSentPacketInfoUPtr packet_info) { SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); // Add to the list int index = static_cast(packet_info->pn_space); - this->_sent_packets[index].insert(std::pair(packet_number, std::move(packet_info))); + this->_sent_packets[index].insert(std::pair(packet_number, std::move(packet_info))); // Increment counters auto ite = this->_sent_packets[index].find(packet_number); if (ite != this->_sent_packets[index].end()) { - if (ite->second->is_crypto_packet) { - ++this->_crypto_outstanding; - ink_assert(this->_crypto_outstanding.load() > 0); - } if (ite->second->ack_eliciting) { ++this->_ack_eliciting_outstanding; ink_assert(this->_ack_eliciting_outstanding.load() > 0); } + if (ite->second->in_flight) { + ++this->_num_packets_in_flight[index]; + } } } -void -QUICLossDetector::_remove_from_sent_packet_list(QUICPacketNumber packet_number, QUICPacketNumberSpace pn_space) -{ - SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); - - auto ite = this->_sent_packets[static_cast(pn_space)].find(packet_number); - this->_decrement_outstanding_counters(ite, pn_space); - this->_sent_packets[static_cast(pn_space)].erase(packet_number); -} - -std::map::iterator -QUICLossDetector::_remove_from_sent_packet_list(std::map::iterator it, +std::pair::iterator> +QUICLossDetector::_remove_from_sent_packet_list(std::map::iterator it, QUICPacketNumberSpace pn_space) { SCOPED_MUTEX_LOCK(lock, this->_loss_detection_mutex, this_ethread()); - this->_decrement_outstanding_counters(it, pn_space); - return this->_sent_packets[static_cast(pn_space)].erase(it); + this->_decrement_counters(it, pn_space); + auto pi = std::move(it->second); + return {std::move(pi), this->_sent_packets[static_cast(pn_space)].erase(it)}; } void -QUICLossDetector::_decrement_outstanding_counters(std::map::iterator it, - QUICPacketNumberSpace pn_space) +QUICLossDetector::_decrement_counters(std::map::iterator it, + QUICPacketNumberSpace pn_space) { if (it != this->_sent_packets[static_cast(pn_space)].end()) { - // Decrement counters - if (it->second->is_crypto_packet) { - ink_assert(this->_crypto_outstanding.load() > 0); - --this->_crypto_outstanding; - } if (it->second->ack_eliciting) { ink_assert(this->_ack_eliciting_outstanding.load() > 0); --this->_ack_eliciting_outstanding; } + --this->_num_packets_in_flight[static_cast(pn_space)]; } } @@ -658,10 +720,11 @@ QUICRTTMeasure::update_rtt(ink_hrtime latest_rtt, ink_hrtime ack_delay) { this->_latest_rtt = latest_rtt; - if (this->_smoothed_rtt == 0) { - this->_min_rtt = 0; - this->_smoothed_rtt = this->_latest_rtt; - this->_rttvar = this->_latest_rtt / 2; + if (this->_is_first_sample) { + this->_min_rtt = this->_latest_rtt; + this->_smoothed_rtt = this->_latest_rtt; + this->_rttvar = this->_latest_rtt / 2; + this->_is_first_sample = false; return; } @@ -698,32 +761,16 @@ QUICRTTMeasure::congestion_period(uint32_t threshold) const return pto * threshold; } -ink_hrtime -QUICRTTMeasure::handshake_retransmit_timeout() const -{ - // Handshake retransmission alarm. - ink_hrtime timeout = 0; - if (this->_smoothed_rtt == 0) { - timeout = 2 * this->_k_initial_rtt; - } else { - timeout = 2 * this->_smoothed_rtt; - } - timeout = std::max(timeout, this->_k_granularity); - timeout = timeout * (1 << this->_crypto_count); - - return timeout; -} - void -QUICRTTMeasure::set_crypto_count(uint32_t count) +QUICRTTMeasure::set_pto_count(uint32_t count) { - this->_crypto_count = count; + this->_pto_count = count; } void -QUICRTTMeasure::set_pto_count(uint32_t count) +QUICRTTMeasure::set_max_ack_delay(ink_hrtime max_ack_delay) { - this->_pto_count = count; + this->_max_ack_delay = max_ack_delay; } ink_hrtime @@ -739,15 +786,15 @@ QUICRTTMeasure::latest_rtt() const } uint32_t -QUICRTTMeasure::crypto_count() const +QUICRTTMeasure::pto_count() const { - return this->_crypto_count; + return this->_pto_count; } -uint32_t -QUICRTTMeasure::pto_count() const +ink_hrtime +QUICRTTMeasure::max_ack_delay() const { - return this->_pto_count; + return this->_max_ack_delay; } ink_hrtime @@ -759,10 +806,10 @@ QUICRTTMeasure::k_granularity() const void QUICRTTMeasure::reset() { - this->_crypto_count = 0; + // A.4. Initialization this->_pto_count = 0; - this->_smoothed_rtt = 0; - this->_rttvar = 0; - this->_min_rtt = 0; this->_latest_rtt = 0; + this->_smoothed_rtt = this->_k_initial_rtt; + this->_rttvar = this->_k_initial_rtt / 2.0; + this->_min_rtt = 0; } diff --git a/iocore/net/quic/QUICLossDetector.h b/iocore/net/quic/QUICLossDetector.h index 891556e7998..5ed1c228b7a 100644 --- a/iocore/net/quic/QUICLossDetector.h +++ b/iocore/net/quic/QUICLossDetector.h @@ -44,70 +44,6 @@ class QUICPinger; class QUICLossDetector; class QUICRTTMeasure; -using QUICPacketInfoUPtr = std::unique_ptr; - -class QUICRTTProvider -{ -public: - virtual ink_hrtime smoothed_rtt() const = 0; - virtual ink_hrtime rttvar() const = 0; - virtual ink_hrtime latest_rtt() const = 0; - - virtual ink_hrtime congestion_period(uint32_t threshold) const = 0; -}; - -class QUICNewRenoCongestionController : public QUICCongestionController -{ -public: - QUICNewRenoCongestionController(QUICContext &context); - virtual ~QUICNewRenoCongestionController() {} - void on_packet_sent(size_t bytes_sent) override; - void on_packet_acked(const QUICPacketInfo &acked_packet) override; - virtual void on_packets_lost(const std::map &packets) override; - void process_ecn(const QUICPacketInfo &acked_largest_packet, const QUICAckFrame::EcnSection *ecn_section) override; - bool check_credit() const; - uint32_t credit() const override; - void reset() override; - bool is_app_limited(); - - // Debug - uint32_t bytes_in_flight() const override; - uint32_t congestion_window() const override; - uint32_t current_ssthresh() const override; - - void add_extra_credit() override; - -private: - Ptr _cc_mutex; - - void _congestion_event(ink_hrtime sent_time); - bool _in_persistent_congestion(const std::map &lost_packets, - QUICPacketInfo *largest_lost_packet); - bool _in_window_lost(const std::map &lost_packets, QUICPacketInfo *largest_lost_packet, - ink_hrtime period) const; - - uint32_t _extra_packets_count = 0; - - // [draft-17 recovery] 7.9.1. Constants of interest - // Values will be loaded from records.config via QUICConfig at constructor - uint32_t _k_max_datagram_size = 0; - uint32_t _k_initial_window = 0; - uint32_t _k_minimum_window = 0; - float _k_loss_reduction_factor = 0.0; - uint32_t _k_persistent_congestion_threshold = 3; - - // [draft-17 recovery] 7.9.2. Variables of interest - uint32_t _ecn_ce_counter = 0; - uint32_t _bytes_in_flight = 0; - uint32_t _congestion_window = 0; - ink_hrtime _congestion_recovery_start_time = 0; - uint32_t _ssthresh = UINT32_MAX; - - bool _in_congestion_recovery(ink_hrtime sent_time) const; - - QUICContext &_context; -}; - class QUICLossDetector : public Continuation, public QUICFrameHandler { public: @@ -117,9 +53,15 @@ class QUICLossDetector : public Continuation, public QUICFrameHandler int event_handler(int event, Event *edata); + // QUICFrameHandler interface std::vector interests() override; virtual QUICConnectionErrorUPtr handle_frame(QUICEncryptionLevel level, const QUICFrame &frame) override; - void on_packet_sent(QUICPacketInfoUPtr packet_info, bool in_flight = true); + + void on_packet_sent(QUICSentPacketInfoUPtr packet_info, bool in_flight = true); + void on_datagram_received(); + // OnPacketNumberSpaceDiscarded is on Congestion Control section but having it here makes more sense because most processes are + // for LD. + void on_packet_number_space_discarded(QUICPacketNumberSpace pn_space); QUICPacketNumber largest_acked_packet_number(QUICPacketNumberSpace pn_space) const; void update_ack_delay_exponent(uint8_t ack_delay_exponent); void reset(); @@ -129,29 +71,32 @@ class QUICLossDetector : public Continuation, public QUICFrameHandler uint8_t _ack_delay_exponent = 3; - // [draft-17 recovery] 6.4.1. Constants of interest + // Recovery A.2. Constants of Interest // Values will be loaded from records.config via QUICConfig at constructor uint32_t _k_packet_threshold = 0; float _k_time_threshold = 0.0; + // kGranularity, kInitialRtt are defined in QUICRTTMeasure + QUICRTTMeasure *_rtt_measure = nullptr; + // kPacketNumberSpace is defined as QUICPacketNumberSpace on QUICTypes.h - // [draft-11 recovery] 3.5.2. Variables of interest + // Recovery A.3. Variables of interest // Keep the order as the same as the spec so that we can see the difference easily. - Action *_loss_detection_timer = nullptr; - ink_hrtime _time_of_last_sent_ack_eliciting_packet = 0; - ink_hrtime _time_of_last_sent_crypto_packet = 0; - ink_hrtime _loss_time[kPacketNumberSpace] = {0}; - QUICPacketNumber _largest_acked_packet[kPacketNumberSpace] = {0}; - std::map _sent_packets[kPacketNumberSpace]; + // latest_rtt, smoothed_rtt, rttvar, min_rtt and max_ack_delay are defined in QUICRttMeasure + Action *_loss_detection_timer = nullptr; + // pto_count is defined in QUICRttMeasure + ink_hrtime _time_of_last_ack_eliciting_packet[QUIC_N_PACKET_SPACES] = {0}; + QUICPacketNumber _largest_acked_packet[QUIC_N_PACKET_SPACES] = {0}; + ink_hrtime _loss_time[QUIC_N_PACKET_SPACES] = {0}; + std::map _sent_packets[QUIC_N_PACKET_SPACES]; // These are not defined on the spec but expected to be count // These counter have to be updated when inserting / erasing packets from _sent_packets with following functions. - std::atomic _crypto_outstanding; std::atomic _ack_eliciting_outstanding; - void _add_to_sent_packet_list(QUICPacketNumber packet_number, std::unique_ptr packet_info); - void _remove_from_sent_packet_list(QUICPacketNumber packet_number, QUICPacketNumberSpace pn_space); - std::map::iterator _remove_from_sent_packet_list( - std::map::iterator it, QUICPacketNumberSpace pn_space); - void _decrement_outstanding_counters(std::map::iterator it, QUICPacketNumberSpace pn_space); + std::atomic _num_packets_in_flight[QUIC_N_PACKET_SPACES]; + void _add_to_sent_packet_list(QUICPacketNumber packet_number, std::unique_ptr packet_info); + std::pair::iterator> _remove_from_sent_packet_list( + std::map::iterator it, QUICPacketNumberSpace pn_space); + void _decrement_counters(std::map::iterator it, QUICPacketNumberSpace pn_space); /* * Because this alarm will be reset on every packet transmission, to reduce number of events, @@ -160,27 +105,28 @@ class QUICLossDetector : public Continuation, public QUICFrameHandler ink_hrtime _loss_detection_alarm_at = 0; void _on_ack_received(const QUICAckFrame &ack_frame, QUICPacketNumberSpace pn_space); - void _on_packet_acked(const QUICPacketInfo &acked_packet); - void _detect_lost_packets(QUICPacketNumberSpace pn_space); + void _on_packet_acked(const QUICSentPacketInfo &acked_packet); + std::map _detect_and_remove_lost_packets(QUICPacketNumberSpace pn_space); void _set_loss_detection_timer(); void _on_loss_detection_timeout(); - void _retransmit_lost_packet(const QUICPacketInfo &packet_info); + void _retransmit_lost_packet(const QUICSentPacketInfo &packet_info); - ink_hrtime _get_earliest_loss_time(QUICPacketNumberSpace &pn_space); + ink_hrtime _get_loss_time_and_space(QUICPacketNumberSpace &space); + ink_hrtime _get_pto_time_and_space(QUICPacketNumberSpace &space); + bool _peer_completed_address_validation() const; - std::vector _determine_newly_acked_packets(const QUICAckFrame &ack_frame, int pn_space); - bool _include_ack_eliciting(const std::vector &acked_packets, int index) const; + std::vector _detect_and_remove_acked_packets(const QUICAckFrame &ack_frame, + QUICPacketNumberSpace pn_space); + bool _include_ack_eliciting(const std::vector &acked_packets) const; - void _retransmit_all_unacked_crypto_data(); - void _send_one_or_two_packet(); - void _send_one_handshake_packets(); - void _send_one_padded_packets(); + void _send_one_or_two_ack_eliciting_packet(QUICPacketNumberSpace pn_space); + void _send_one_ack_eliciting_handshake_packet(); + void _send_one_ack_eliciting_padded_initial_packet(); void _send_packet(QUICEncryptionLevel level, bool padded = false); bool _is_client_without_one_rtt_key() const; - QUICRTTMeasure *_rtt_measure = nullptr; QUICPinger *_pinger = nullptr; QUICPadder *_padder = nullptr; QUICCongestionController *_cc = nullptr; @@ -200,7 +146,6 @@ class QUICRTTMeasure : public QUICRTTProvider void init(const QUICLDConfig &ld_config); // period - ink_hrtime handshake_retransmit_timeout() const; ink_hrtime current_pto_period() const; ink_hrtime congestion_period(uint32_t threshold) const override; @@ -210,10 +155,10 @@ class QUICRTTMeasure : public QUICRTTProvider ink_hrtime latest_rtt() const override; uint32_t pto_count() const; - uint32_t crypto_count() const; + ink_hrtime max_ack_delay() const; - void set_crypto_count(uint32_t count); void set_pto_count(uint32_t count); + void set_max_ack_delay(ink_hrtime max_ack_delay); void update_rtt(ink_hrtime latest_rtt, ink_hrtime ack_delay); void reset(); @@ -221,19 +166,18 @@ class QUICRTTMeasure : public QUICRTTProvider ink_hrtime k_granularity() const; private: - // related to rtt calculate - uint32_t _crypto_count = 0; - uint32_t _pto_count = 0; - // FIXME should be set by transport parameters - ink_hrtime _max_ack_delay = HRTIME_MSECONDS(25); + bool _is_first_sample = false; - // rtt vars + // A.3. Variables of interest ink_hrtime _latest_rtt = 0; ink_hrtime _smoothed_rtt = 0; ink_hrtime _rttvar = 0; ink_hrtime _min_rtt = INT64_MAX; + // FIXME should be set by transport parameters + ink_hrtime _max_ack_delay = HRTIME_MSECONDS(25); + uint32_t _pto_count = 0; - // config + // Recovery A.2. Constants of Interest ink_hrtime _k_granularity = 0; ink_hrtime _k_initial_rtt = HRTIME_MSECONDS(500); }; diff --git a/iocore/net/quic/QUICNewRenoCongestionController.cc b/iocore/net/quic/QUICNewRenoCongestionController.cc index 2658443c385..7745670082d 100644 --- a/iocore/net/quic/QUICNewRenoCongestionController.cc +++ b/iocore/net/quic/QUICNewRenoCongestionController.cc @@ -22,19 +22,26 @@ */ #include -#include +#include +#include #define QUICCCDebug(fmt, ...) \ Debug("quic_cc", \ "[%s] " \ - "window: %" PRIu32 " bytes: %" PRIu32 " ssthresh: %" PRIu32 " extra: %" PRIu32 " " fmt, \ + "window:%" PRIu32 " in-flight:%" PRIu32 " ssthresh:%" PRIu32 " extra:%" PRIu32 " " fmt, \ + this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \ + this->_extra_packets_count, ##__VA_ARGS__) +#define QUICCCVDebug(fmt, ...) \ + Debug("v_quic_cc", \ + "[%s] " \ + "window:%" PRIu32 " in-flight:%" PRIu32 " ssthresh:%" PRIu32 " extra:%" PRIu32 " " fmt, \ this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \ this->_extra_packets_count, ##__VA_ARGS__) #define QUICCCError(fmt, ...) \ Error("quic_cc", \ "[%s] " \ - "window: %" PRIu32 " bytes: %" PRIu32 " ssthresh: %" PRIu32 " extra %" PRIu32 " " fmt, \ + "window:%" PRIu32 " in-flight:%" PRIu32 " ssthresh:%" PRIu32 " extra:%" PRIu32 " " fmt, \ this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \ this->_extra_packets_count, ##__VA_ARGS__) @@ -42,7 +49,6 @@ QUICNewRenoCongestionController::QUICNewRenoCongestionController(QUICContext &co : _cc_mutex(new_ProxyMutex()), _context(context) { auto &cc_config = context.cc_config(); - this->_k_max_datagram_size = cc_config.max_datagram_size(); this->_k_initial_window = cc_config.initial_window(); this->_k_minimum_window = cc_config.minimum_window(); this->_k_loss_reduction_factor = cc_config.loss_reduction_factor(); @@ -69,46 +75,42 @@ QUICNewRenoCongestionController::_in_congestion_recovery(ink_hrtime sent_time) c } bool -QUICNewRenoCongestionController::is_app_limited() +QUICNewRenoCongestionController::_is_app_or_flow_control_limited() { // FIXME : don't known how does app worked here return false; } void -QUICNewRenoCongestionController::on_packet_acked(const QUICPacketInfo &acked_packet) +QUICNewRenoCongestionController::_maybe_send_one_packet() { - // Remove from bytes_in_flight. - SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread()); - this->_bytes_in_flight -= acked_packet.sent_bytes; - if (this->_in_congestion_recovery(acked_packet.time_sent)) { - // Do not increase congestion window in recovery period. - return; - } + // TODO Implement _maybe_send_one_packet +} - if (this->is_app_limited()) { - // Do not increase congestion_window if application - // limited. - return; - } +bool +QUICNewRenoCongestionController::_are_all_packets_lost(const std::map &lost_packets, + const QUICSentPacketInfoUPtr &largest_lost_packet, ink_hrtime period) const +{ + // check whether packets are continuous. return true if all continuous packets are in period + QUICPacketNumber next_expected = UINT64_MAX; + for (auto &it : lost_packets) { + if (it.second->time_sent >= largest_lost_packet->time_sent - period) { + if (next_expected == UINT64_MAX) { + next_expected = it.second->packet_number + 1; + continue; + } - if (this->_congestion_window < this->_ssthresh) { - // Slow start. - this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::SLOW_START); - this->_congestion_window += acked_packet.sent_bytes; - QUICCCDebug("slow start window changed"); - } else { - // Congestion avoidance. - this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, - QUICCongestionController::State::CONGESTION_AVOIDANCE); - this->_congestion_window += this->_k_max_datagram_size * acked_packet.sent_bytes / this->_congestion_window; - QUICCCDebug("Congestion avoidance window changed"); + if (next_expected != it.second->packet_number) { + return false; + } + + next_expected = it.second->packet_number + 1; + } } + + return next_expected == UINT64_MAX ? false : true; } -// additional code -// the original one is: -// CongestionEvent(sent_time): void QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time) { @@ -122,54 +124,82 @@ QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time) this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::RECOVERY); this->_context.trigger(QUICContext::CallbackEvent::METRICS_UPDATE, this->_congestion_window, this->_bytes_in_flight, this->_ssthresh); + // A packet can be sent to speed up loss recovery. + this->_maybe_send_one_packet(); } } -// additional code -// the original one is: -// ProcessECN(ack): void -QUICNewRenoCongestionController::process_ecn(const QUICPacketInfo &acked_largest_packet, - const QUICAckFrame::EcnSection *ecn_section) +QUICNewRenoCongestionController::process_ecn(const QUICAckFrame &ack_frame, QUICPacketNumberSpace pn_space, + ink_hrtime largest_acked_time_sent) { // If the ECN-CE counter reported by the peer has increased, // this could be a new congestion event. - if (ecn_section->ecn_ce_count() > this->_ecn_ce_counter) { - this->_ecn_ce_counter = ecn_section->ecn_ce_count(); + if (ack_frame.ecn_section()->ecn_ce_count() > this->_ecn_ce_counters[static_cast(pn_space)]) { + this->_ecn_ce_counters[static_cast(pn_space)] = ack_frame.ecn_section()->ecn_ce_count(); // Start a new congestion event if the last acknowledged // packet was sent after the start of the previous // recovery epoch. - this->_congestion_event(acked_largest_packet.time_sent); + this->_congestion_event(largest_acked_time_sent); } } bool -QUICNewRenoCongestionController::_in_persistent_congestion(const std::map &lost_packets, - QUICPacketInfo *largest_lost_packet) +QUICNewRenoCongestionController::_in_persistent_congestion(const std::map &lost_packets, + const QUICSentPacketInfoUPtr &largest_lost_packet) { - ink_hrtime period = this->_context.rtt_provider()->congestion_period(this->_k_persistent_congestion_threshold); - // Determine if all packets in the window before the - // newest lost packet, including the edges, are marked - // lost - return this->_in_window_lost(lost_packets, largest_lost_packet, period); + ink_hrtime congestion_period = this->_context.rtt_provider()->congestion_period(this->_k_persistent_congestion_threshold); + // Determine if all packets in the time period before the + // largest newly lost packet, including the edges, are + // marked lost + return this->_are_all_packets_lost(lost_packets, largest_lost_packet, congestion_period); +} + +void +QUICNewRenoCongestionController::on_packets_acked(const std::vector &packets) +{ + SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread()); + + for (auto &packet : packets) { + // Remove from bytes_in_flight. + this->_bytes_in_flight -= packet->sent_bytes; + if (this->_in_congestion_recovery(packet->time_sent)) { + // Do not increase congestion window in recovery period. + continue; + } + if (this->_is_app_or_flow_control_limited()) { + // Do not increase congestion_window if application + // limited or flow control limited. + continue; + } + if (this->_congestion_window < this->_ssthresh) { + // Slow start. + this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::SLOW_START); + this->_congestion_window += packet->sent_bytes; + QUICCCVDebug("slow start window changed"); + continue; + } + // Congestion avoidance. + this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, + QUICCongestionController::State::CONGESTION_AVOIDANCE); + this->_congestion_window += this->_max_datagram_size * static_cast(packet->sent_bytes) / this->_congestion_window; + QUICCCVDebug("Congestion avoidance window changed"); + } } // additional code // the original one is: // OnPacketsLost(lost_packets): void -QUICNewRenoCongestionController::on_packets_lost(const std::map &lost_packets) +QUICNewRenoCongestionController::on_packets_lost(const std::map &lost_packets) { - if (lost_packets.empty()) { - return; - } - SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread()); + // Remove lost packets from bytes_in_flight. for (auto &lost_packet : lost_packets) { this->_bytes_in_flight -= lost_packet.second->sent_bytes; } - QUICPacketInfo *largest_lost_packet = lost_packets.rbegin()->second; + const auto &largest_lost_packet = lost_packets.rbegin()->second; this->_congestion_event(largest_lost_packet->time_sent); // Collapse congestion window if persistent congestion @@ -178,8 +208,14 @@ QUICNewRenoCongestionController::on_packets_lost(const std::map_bytes_in_flight -= bytes_in_flight; +} + bool -QUICNewRenoCongestionController::check_credit() const +QUICNewRenoCongestionController::_check_credit() const { if (this->_bytes_in_flight >= this->_congestion_window) { QUICCCDebug("Congestion control pending"); @@ -195,7 +231,7 @@ QUICNewRenoCongestionController::credit() const return UINT32_MAX; } - if (this->check_credit()) { + if (this->_check_credit()) { return this->_congestion_window - this->_bytes_in_flight; } else { return 0; @@ -226,34 +262,13 @@ QUICNewRenoCongestionController::reset() { SCOPED_MUTEX_LOCK(lock, this->_cc_mutex, this_ethread()); - this->_bytes_in_flight = 0; this->_congestion_window = this->_k_initial_window; + this->_bytes_in_flight = 0; this->_congestion_recovery_start_time = 0; this->_ssthresh = UINT32_MAX; -} - -bool -QUICNewRenoCongestionController::_in_window_lost(const std::map &lost_packets, - QUICPacketInfo *largest_lost_packet, ink_hrtime period) const -{ - // check whether packets are continuous. return true if all continuous packets are in period - QUICPacketNumber next_expected = UINT64_MAX; - for (auto &it : lost_packets) { - if (it.second->time_sent >= largest_lost_packet->time_sent - period) { - if (next_expected == UINT64_MAX) { - next_expected = it.second->packet_number + 1; - continue; - } - - if (next_expected != it.second->packet_number) { - return false; - } - - next_expected = it.second->packet_number + 1; - } + for (int i = 0; i < QUIC_N_PACKET_SPACES; ++i) { + this->_ecn_ce_counters[i] = 0; } - - return next_expected == UINT64_MAX ? false : true; } void diff --git a/iocore/net/quic/QUICNewRenoCongestionController.h b/iocore/net/quic/QUICNewRenoCongestionController.h new file mode 100644 index 00000000000..43a870ccc53 --- /dev/null +++ b/iocore/net/quic/QUICNewRenoCongestionController.h @@ -0,0 +1,81 @@ +/** @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. + */ + +#pragma once + +#include "QUICTypes.h" +#include "QUICContext.h" +#include "QUICCongestionController.h" + +class QUICNewRenoCongestionController : public QUICCongestionController +{ +public: + QUICNewRenoCongestionController(QUICContext &context); + virtual ~QUICNewRenoCongestionController() {} + + void on_packet_sent(size_t bytes_sent) override; + void on_packets_acked(const std::vector &packets) override; + virtual void on_packets_lost(const std::map &packets) override; + void on_packet_number_space_discarded(size_t bytes_in_flight) override; + void process_ecn(const QUICAckFrame &ack, QUICPacketNumberSpace pn_space, ink_hrtime largest_acked_packet_time_sent) override; + uint32_t credit() const override; + void reset() override; + + // Debug + uint32_t bytes_in_flight() const override; + uint32_t congestion_window() const override; + uint32_t current_ssthresh() const override; + + void add_extra_credit() override; + +private: + Ptr _cc_mutex; + uint32_t _extra_packets_count = 0; + QUICContext &_context; + bool _check_credit() const; + + // Appendix B. Congestion Control Pseudocode + bool _in_congestion_recovery(ink_hrtime sent_time) const; + void _congestion_event(ink_hrtime sent_time); + bool _in_persistent_congestion(const std::map &lost_packets, + const QUICSentPacketInfoUPtr &largest_lost_packet); + bool _is_app_or_flow_control_limited(); + void _maybe_send_one_packet(); + bool _are_all_packets_lost(const std::map &lost_packets, + const QUICSentPacketInfoUPtr &largest_lost_packet, ink_hrtime period) const; + + // Recovery B.1. Constants of interest + // Values will be loaded from records.config via QUICConfig at constructor + uint32_t _k_initial_window = 0; + uint32_t _k_minimum_window = 0; + float _k_loss_reduction_factor = 0.0; + uint32_t _k_persistent_congestion_threshold = 0; + + // B.2. Variables of interest + uint32_t _max_datagram_size = 0; + uint32_t _ecn_ce_counters[QUIC_N_PACKET_SPACES] = {0}; + uint32_t _bytes_in_flight = 0; + uint32_t _congestion_window = 0; + ink_hrtime _congestion_recovery_start_time = 0; + uint32_t _ssthresh = UINT32_MAX; +}; diff --git a/iocore/net/quic/QUICPacket.cc b/iocore/net/quic/QUICPacket.cc index 0bcf624adbe..5f5707dceed 100644 --- a/iocore/net/quic/QUICPacket.cc +++ b/iocore/net/quic/QUICPacket.cc @@ -883,9 +883,14 @@ QUICStatelessResetPacketR::destination_cid() const // // QUICVersionNegotiationPacket // -QUICVersionNegotiationPacket::QUICVersionNegotiationPacket(QUICConnectionId dcid, QUICConnectionId scid, - const QUICVersion versions[], int nversions) - : QUICLongHeaderPacket(0, dcid, scid, false, false, false), _versions(versions), _nversions(nversions) + +QUICVersionNegotiationPacket::QUICVersionNegotiationPacket(const QUICConnectionId &dcid, const QUICConnectionId &scid, + const QUICVersion versions[], int nversions, + QUICVersion version_in_initial) + : QUICLongHeaderPacket(0, dcid, scid, false, false, false), + _versions(versions), + _nversions(nversions), + _version_in_initial(version_in_initial) { } @@ -961,7 +966,11 @@ QUICVersionNegotiationPacket::payload_block() const // [draft-18] 6.3. Using Reserved Versions // To help ensure this, a server SHOULD include a reserved version (see Section 15) while generating a // Version Negotiation packet. - QUICTypeUtil::write_QUICVersion(QUIC_EXERCISE_VERSION, buf + written_len, &n); + QUICVersion exersice_version = QUIC_EXERCISE_VERSION1; + if (this->_version_in_initial == QUIC_EXERCISE_VERSION1) { + exersice_version = QUIC_EXERCISE_VERSION2; + } + QUICTypeUtil::write_QUICVersion(exersice_version, buf + written_len, &n); written_len += n; block->fill(written_len); @@ -1736,7 +1745,7 @@ QUICRetryPacket::payload_block() const block->fill(written_len); // Retry Integrity Tag - QUICRetryIntegrityTag::compute(buf + written_len, this->_token.original_dcid(), this->header_block(), block); + QUICRetryIntegrityTag::compute(buf + written_len, this->version(), this->_token.original_dcid(), this->header_block(), block); written_len += QUICRetryIntegrityTag::LEN; block->fill(QUICRetryIntegrityTag::LEN); @@ -1838,7 +1847,7 @@ bool QUICRetryPacketR::has_valid_tag(const QUICConnectionId &odcid) const { uint8_t tag_computed[QUICRetryIntegrityTag::LEN]; - QUICRetryIntegrityTag::compute(tag_computed, odcid, this->_header_block, this->_payload_block_without_tag); + QUICRetryIntegrityTag::compute(tag_computed, this->version(), odcid, this->_header_block, this->_payload_block_without_tag); return memcmp(this->_integrity_tag, tag_computed, QUICRetryIntegrityTag::LEN) == 0; } diff --git a/iocore/net/quic/QUICPacket.h b/iocore/net/quic/QUICPacket.h index a62daa429b0..e9d02127328 100644 --- a/iocore/net/quic/QUICPacket.h +++ b/iocore/net/quic/QUICPacket.h @@ -319,7 +319,8 @@ class QUICVersionNegotiationPacket : public QUICLongHeaderPacket /** * For sending packet */ - QUICVersionNegotiationPacket(QUICConnectionId dcid, QUICConnectionId scid, const QUICVersion versions[], int nversions); + QUICVersionNegotiationPacket(const QUICConnectionId &dcid, const QUICConnectionId &scid, const QUICVersion versions[], + int nversions, QUICVersion version_in_initial); QUICPacketType type() const override; QUICVersion version() const override; @@ -335,6 +336,7 @@ class QUICVersionNegotiationPacket : public QUICLongHeaderPacket private: const QUICVersion *_versions; int _nversions; + const QUICVersion _version_in_initial; }; class QUICVersionNegotiationPacketR : public QUICLongHeaderPacketR diff --git a/iocore/net/quic/QUICPacketFactory.cc b/iocore/net/quic/QUICPacketFactory.cc index 436c68306b1..2fcb1a29549 100644 --- a/iocore/net/quic/QUICPacketFactory.cc +++ b/iocore/net/quic/QUICPacketFactory.cc @@ -197,10 +197,11 @@ QUICPacketFactory::create(uint8_t *packet_buf, UDPConnection *udp_con, IpEndpoin } QUICPacketUPtr -QUICPacketFactory::create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid) +QUICPacketFactory::create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid, QUICVersion version_in_initial) { - return QUICPacketUPtr(new QUICVersionNegotiationPacket(dcid, scid, QUIC_SUPPORTED_VERSIONS, countof(QUIC_SUPPORTED_VERSIONS)), - &QUICPacketDeleter::delete_packet_new); + return QUICPacketUPtr( + new QUICVersionNegotiationPacket(dcid, scid, QUIC_SUPPORTED_VERSIONS, countof(QUIC_SUPPORTED_VERSIONS), version_in_initial), + &QUICPacketDeleter::delete_packet_new); } QUICPacketUPtr @@ -228,10 +229,10 @@ QUICPacketFactory::create_initial_packet(uint8_t *packet_buf, QUICConnectionId d } QUICPacketUPtr -QUICPacketFactory::create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICRetryToken &token) +QUICPacketFactory::create_retry_packet(QUICVersion version, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICRetryToken &token) { - return QUICPacketUPtr(new QUICRetryPacket(QUIC_SUPPORTED_VERSIONS[0], destination_cid, source_cid, token), - &QUICPacketDeleter::delete_packet_new); + return QUICPacketUPtr(new QUICRetryPacket(version, destination_cid, source_cid, token), &QUICPacketDeleter::delete_packet_new); } QUICPacketUPtr @@ -329,7 +330,7 @@ QUICPacketFactory::is_ready_to_create_protected_packet() void QUICPacketFactory::reset() { - for (auto i = 0; i < kPacketNumberSpace; i++) { + for (auto i = 0; i < QUIC_N_PACKET_SPACES; i++) { this->_packet_number_generator[i].reset(); } } diff --git a/iocore/net/quic/QUICPacketFactory.h b/iocore/net/quic/QUICPacketFactory.h index d5316ce02f5..2d59de66c9f 100644 --- a/iocore/net/quic/QUICPacketFactory.h +++ b/iocore/net/quic/QUICPacketFactory.h @@ -44,9 +44,11 @@ class QUICPacketFactory { public: static QUICPacketUPtr create_null_packet(); - static QUICPacketUPtr create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid); + static QUICPacketUPtr create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid, + QUICVersion version_in_version); static QUICPacketUPtr create_stateless_reset_packet(QUICStatelessResetToken stateless_reset_token, size_t maximum_size); - static QUICPacketUPtr create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICRetryToken &token); + static QUICPacketUPtr create_retry_packet(QUICVersion version, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICRetryToken &token); QUICPacketFactory(const QUICPacketProtectionKeyInfo &pp_key_info) : _pp_key_info(pp_key_info), _pp_protector(pp_key_info) {} diff --git a/iocore/net/quic/QUICRetryIntegrityTag.cc b/iocore/net/quic/QUICRetryIntegrityTag.cc index c528656896d..117dff696a4 100644 --- a/iocore/net/quic/QUICRetryIntegrityTag.cc +++ b/iocore/net/quic/QUICRetryIntegrityTag.cc @@ -24,7 +24,8 @@ #include "QUICRetryIntegrityTag.h" bool -QUICRetryIntegrityTag::compute(uint8_t *out, QUICConnectionId odcid, Ptr header, Ptr payload) +QUICRetryIntegrityTag::compute(uint8_t *out, QUICVersion version, QUICConnectionId odcid, Ptr header, + Ptr payload) { EVP_CIPHER_CTX *aead_ctx; @@ -37,7 +38,23 @@ QUICRetryIntegrityTag::compute(uint8_t *out, QUICConnectionId odcid, Ptr header, Ptr payload); + static bool compute(uint8_t *out, QUICVersion version, QUICConnectionId odcid, Ptr header, + Ptr payload); private: - static constexpr uint8_t KEY_FOR_RETRY_INTEGRITY_TAG[] = {0x4d, 0x32, 0xec, 0xdb, 0x2a, 0x21, 0x33, 0xc8, - 0x41, 0xe4, 0x04, 0x3d, 0xf2, 0x7d, 0x44, 0x30}; - static constexpr uint8_t NONCE_FOR_RETRY_INTEGRITY_TAG[] = {0x4d, 0x16, 0x11, 0xd0, 0x55, 0x13, - 0xa5, 0x52, 0xc5, 0x87, 0xd5, 0x75}; + static constexpr uint8_t KEY_FOR_RETRY_INTEGRITY_TAG[] = {0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, + 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1}; + static constexpr uint8_t NONCE_FOR_RETRY_INTEGRITY_TAG[] = {0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, + 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c}; + // For draft 27 + static constexpr uint8_t KEY_FOR_RETRY_INTEGRITY_TAG_D27[] = {0x4d, 0x32, 0xec, 0xdb, 0x2a, 0x21, 0x33, 0xc8, + 0x41, 0xe4, 0x04, 0x3d, 0xf2, 0x7d, 0x44, 0x30}; + static constexpr uint8_t NONCE_FOR_RETRY_INTEGRITY_TAG_D27[] = {0x4d, 0x16, 0x11, 0xd0, 0x55, 0x13, + 0xa5, 0x52, 0xc5, 0x87, 0xd5, 0x75}; }; diff --git a/iocore/net/quic/QUICStream.h b/iocore/net/quic/QUICStream.h index bff660a0b6d..afeb03c4a23 100644 --- a/iocore/net/quic/QUICStream.h +++ b/iocore/net/quic/QUICStream.h @@ -130,5 +130,8 @@ class QUICStreamVConnection : public VConnection, public QUICStream #define QUICStreamFCDebug(fmt, ...) \ Debug("quic_flow_ctrl", "[%s] [%" PRIu64 "] [%s] " fmt, this->_connection_info->cids().data(), this->_id, \ QUICDebugNames::stream_state(this->_state.get()), ##__VA_ARGS__) +#define QUICVStreamFCDebug(fmt, ...) \ + Debug("v_quic_flow_ctrl", "[%s] [%" PRIu64 "] [%s] " fmt, this->_connection_info->cids().data(), this->_id, \ + QUICDebugNames::stream_state(this->_state.get()), ##__VA_ARGS__) extern const uint32_t MAX_STREAM_FRAME_OVERHEAD; diff --git a/iocore/net/quic/QUICTLS.cc b/iocore/net/quic/QUICTLS.cc index 4764ea1962e..a618e3add0a 100644 --- a/iocore/net/quic/QUICTLS.cc +++ b/iocore/net/quic/QUICTLS.cc @@ -165,7 +165,7 @@ QUICTLS::is_ready_to_derive() const } int -QUICTLS::initialize_key_materials(QUICConnectionId cid) +QUICTLS::initialize_key_materials(QUICConnectionId cid, QUICVersion version) { this->_pp_key_info.set_cipher_initial(EVP_aes_128_gcm()); this->_pp_key_info.set_cipher_for_hp_initial(EVP_aes_128_ecb()); @@ -217,8 +217,8 @@ QUICTLS::initialize_key_materials(QUICConnectionId cid) server_iv_len = this->_pp_key_info.decryption_iv_len(QUICKeyPhase::INITIAL); } - this->_keygen_for_client.generate(client_key_for_hp, client_key, client_iv, client_iv_len, cid); - this->_keygen_for_server.generate(server_key_for_hp, server_key, server_iv, server_iv_len, cid); + this->_keygen_for_client.generate(version, client_key_for_hp, client_key, client_iv, client_iv_len, cid); + this->_keygen_for_server.generate(version, server_key_for_hp, server_key, server_iv, server_iv_len, cid); this->_pp_key_info.set_decryption_key_available(QUICKeyPhase::INITIAL); this->_pp_key_info.set_encryption_key_available(QUICKeyPhase::INITIAL); diff --git a/iocore/net/quic/QUICTLS.h b/iocore/net/quic/QUICTLS.h index e60d9206c8f..938e1e2d3a0 100644 --- a/iocore/net/quic/QUICTLS.h +++ b/iocore/net/quic/QUICTLS.h @@ -71,7 +71,7 @@ class QUICTLS : public QUICHandshakeProtocol void reset() override; bool is_handshake_finished() const override; bool is_ready_to_derive() const override; - int initialize_key_materials(QUICConnectionId cid) override; + int initialize_key_materials(QUICConnectionId cid, QUICVersion version) override; void update_negotiated_cipher(); void update_key_materials_for_read(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len); void update_key_materials_for_write(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len); diff --git a/iocore/net/quic/QUICTLS_boringssl.cc b/iocore/net/quic/QUICTLS_boringssl.cc index 46f70f0c633..01aabfa1546 100644 --- a/iocore/net/quic/QUICTLS_boringssl.cc +++ b/iocore/net/quic/QUICTLS_boringssl.cc @@ -30,6 +30,7 @@ #include #include "QUICGlobals.h" +#include "QUICConnection.h" #include "QUICPacketProtectionKeyInfo.h" static constexpr char tag[] = "quic_tls"; @@ -80,10 +81,13 @@ set_write_secret(SSL *ssl, enum ssl_encryption_level_t level, const SSL_CIPHER * const uint8_t *tp_buf; size_t tp_buf_len; SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + const QUICConnection *qc = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_qc_index)); + QUICVersion version = qc->negotiated_version(); if (SSL_is_server(ssl)) { - qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len, version)); } else { - qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + qtls->set_remote_transport_parameters( + std::make_shared(tp_buf, tp_buf_len, version)); } } @@ -111,10 +115,13 @@ set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, const uint8_ const uint8_t *tp_buf; size_t tp_buf_len; SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + const QUICConnection *qc = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_qc_index)); + QUICVersion version = qc->negotiated_version(); if (SSL_is_server(ssl)) { - qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len, version)); } else { - qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + qtls->set_remote_transport_parameters( + std::make_shared(tp_buf, tp_buf_len, version)); } } diff --git a/iocore/net/quic/QUICTLS_openssl.cc b/iocore/net/quic/QUICTLS_openssl.cc index 28dfbf1a5b7..c34598dd3f6 100644 --- a/iocore/net/quic/QUICTLS_openssl.cc +++ b/iocore/net/quic/QUICTLS_openssl.cc @@ -29,6 +29,7 @@ #include #include "QUICGlobals.h" +#include "QUICConnection.h" #include "QUICPacketProtectionKeyInfo.h" static constexpr char tag[] = "quic_tls"; @@ -71,10 +72,13 @@ set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, const uint8_ const uint8_t *tp_buf; size_t tp_buf_len; SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + const QUICConnection *qc = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_qc_index)); + QUICVersion version = qc->negotiated_version(); if (SSL_is_server(ssl)) { - qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len, version)); } else { - qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + qtls->set_remote_transport_parameters( + std::make_shared(tp_buf, tp_buf_len, version)); } } diff --git a/iocore/net/quic/QUICTransportParameters.cc b/iocore/net/quic/QUICTransportParameters.cc index cead9700e2d..c11345818f2 100644 --- a/iocore/net/quic/QUICTransportParameters.cc +++ b/iocore/net/quic/QUICTransportParameters.cc @@ -35,9 +35,9 @@ static constexpr char tag[] = "quic_handshake"; static constexpr int TRANSPORT_PARAMETERS_MAXIMUM_SIZE = 65535; -static constexpr uint32_t TP_ERROR_LENGTH = 0x010000; -static constexpr uint32_t TP_ERROR_VALUE = 0x020000; -// static constexpr uint32_t TP_ERROR_MUST_EXIST = 0x030000; +static constexpr uint32_t TP_ERROR_LENGTH = 0x010000; +static constexpr uint32_t TP_ERROR_VALUE = 0x020000; +static constexpr uint32_t TP_ERROR_MUST_EXIST = 0x030000; static constexpr uint32_t TP_ERROR_MUST_NOT_EXIST = 0x040000; QUICTransportParameters::Value::Value(const uint8_t *data, uint16_t len) : _len(len) @@ -70,6 +70,14 @@ QUICTransportParameters::Value::len() const return this->_len; } +QUICTransportParameters::QUICTransportParameters(const uint8_t *buf, size_t len, QUICVersion version) +{ + this->_load(buf, len, version); + if (is_debug_tag_set(tag)) { + this->_print(); + } +} + QUICTransportParameters::~QUICTransportParameters() { for (auto p : this->_parameters) { @@ -78,7 +86,7 @@ QUICTransportParameters::~QUICTransportParameters() } void -QUICTransportParameters::_load(const uint8_t *buf, size_t len) +QUICTransportParameters::_load(const uint8_t *buf, size_t len, QUICVersion version) { bool has_error = false; const uint8_t *p = buf; @@ -130,7 +138,7 @@ QUICTransportParameters::_load(const uint8_t *buf, size_t len) } // Validate parameters - int res = this->_validate_parameters(); + int res = this->_validate_parameters(version); if (res < 0) { Debug(tag, "Transport parameter is not valid (err=%d)", res); this->_valid = false; @@ -140,7 +148,7 @@ QUICTransportParameters::_load(const uint8_t *buf, size_t len) } int -QUICTransportParameters::_validate_parameters() const +QUICTransportParameters::_validate_parameters(QUICVersion version) const { decltype(this->_parameters)::const_iterator ite; @@ -159,9 +167,9 @@ QUICTransportParameters::_validate_parameters() const if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_IDLE_TIMEOUT)) != this->_parameters.end()) { } - if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_PACKET_SIZE)) != this->_parameters.end()) { + if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_UDP_PAYLOAD_SIZE)) != this->_parameters.end()) { if (QUICIntUtil::read_nbytes_as_uint(ite->second->data(), ite->second->len()) < 1200) { - return -(TP_ERROR_VALUE | QUICTransportParameterId::MAX_PACKET_SIZE); + return -(TP_ERROR_VALUE | QUICTransportParameterId::MAX_UDP_PAYLOAD_SIZE); } } @@ -262,6 +270,10 @@ QUICTransportParameters::store(uint8_t *buf, uint16_t *len) const } *len = (p - buf); + + if (is_debug_tag_set(tag)) { + this->_print(); + } } void @@ -301,12 +313,9 @@ QUICTransportParameters::_print() const // QUICTransportParametersInClientHello // -QUICTransportParametersInClientHello::QUICTransportParametersInClientHello(const uint8_t *buf, size_t len) +QUICTransportParametersInClientHello::QUICTransportParametersInClientHello(const uint8_t *buf, size_t len, QUICVersion version) + : QUICTransportParameters(buf, len, version) { - this->_load(buf, len); - if (is_debug_tag_set(tag)) { - this->_print(); - } } std::ptrdiff_t @@ -316,9 +325,9 @@ QUICTransportParametersInClientHello::_parameters_offset(const uint8_t *) const } int -QUICTransportParametersInClientHello::_validate_parameters() const +QUICTransportParametersInClientHello::_validate_parameters(QUICVersion version) const { - int res = QUICTransportParameters::_validate_parameters(); + int res = QUICTransportParameters::_validate_parameters(version); if (res < 0) { return res; } @@ -326,14 +335,22 @@ QUICTransportParametersInClientHello::_validate_parameters() const decltype(this->_parameters)::const_iterator ite; // MUST NOTs - if ((ite = this->_parameters.find(QUICTransportParameterId::STATELESS_RESET_TOKEN)) != this->_parameters.end()) { - return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::STATELESS_RESET_TOKEN); + if ((ite = this->_parameters.find(QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID)) != this->_parameters.end()) { + return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID); } if ((ite = this->_parameters.find(QUICTransportParameterId::PREFERRED_ADDRESS)) != this->_parameters.end()) { return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::PREFERRED_ADDRESS); } + if ((ite = this->_parameters.find(QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID)) != this->_parameters.end()) { + return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID); + } + + if ((ite = this->_parameters.find(QUICTransportParameterId::STATELESS_RESET_TOKEN)) != this->_parameters.end()) { + return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::STATELESS_RESET_TOKEN); + } + return 0; } @@ -341,18 +358,10 @@ QUICTransportParametersInClientHello::_validate_parameters() const // QUICTransportParametersInEncryptedExtensions // -QUICTransportParametersInEncryptedExtensions::QUICTransportParametersInEncryptedExtensions(const uint8_t *buf, size_t len) -{ - this->_load(buf, len); - if (is_debug_tag_set(tag)) { - this->_print(); - } -} - -void -QUICTransportParametersInEncryptedExtensions::add_version(QUICVersion version) +QUICTransportParametersInEncryptedExtensions::QUICTransportParametersInEncryptedExtensions(const uint8_t *buf, size_t len, + QUICVersion version) + : QUICTransportParameters(buf, len, version) { - this->_versions[this->_n_versions++] = version; } std::ptrdiff_t @@ -362,21 +371,36 @@ QUICTransportParametersInEncryptedExtensions::_parameters_offset(const uint8_t * } int -QUICTransportParametersInEncryptedExtensions::_validate_parameters() const +QUICTransportParametersInEncryptedExtensions::_validate_parameters(QUICVersion version) const { - int res = QUICTransportParameters::_validate_parameters(); + int res = QUICTransportParameters::_validate_parameters(version); if (res < 0) { return res; } decltype(this->_parameters)::const_iterator ite; - // MUSTs if the server sent a Retry packet - if ((ite = this->_parameters.find(QUICTransportParameterId::ORIGINAL_CONNECTION_ID)) != this->_parameters.end()) { - // We cannot check the length because it's not a fixed length. - } else { - // TODO Need a way that checks if we received a Retry from the server - // return -(TP_ERROR_MUST_EXIST | QUICTransportParameterId::ORIGINAL_CONNECTION_ID); + // MUSTs + if (version == QUIC_SUPPORTED_VERSIONS[0]) { // draft-28 + if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_SOURCE_CONNECTION_ID)) != this->_parameters.end()) { + // We cannot check the length because it's not a fixed length. + } else { + return -(TP_ERROR_MUST_EXIST | QUICTransportParameterId::INITIAL_SOURCE_CONNECTION_ID); + } + + if ((ite = this->_parameters.find(QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID)) != this->_parameters.end()) { + // We cannot check the length because it's not a fixed length. + } else { + return -(TP_ERROR_MUST_EXIST | QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID); + } + + // MUSTs if the server sent a Retry packet, but MUST NOT if the server did not send a Retry packet + // TODO Check if the server sent Retry packet + if ((ite = this->_parameters.find(QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID)) != this->_parameters.end()) { + // return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID); + } else { + // return -(TP_ERROR_MUST_EXIST | QUICTransportParameterId::RETRY_SOURCE_CONNECTION_ID); + } } // MAYs @@ -422,14 +446,15 @@ int QUICTransportParametersHandler::parse(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char *in, size_t inlen, X509 *x, size_t chainidx, int *al, void *parse_arg) { - QUICTLS *qtls = static_cast(SSL_get_ex_data(s, QUIC::ssl_quic_tls_index)); - + QUICTLS *qtls = static_cast(SSL_get_ex_data(s, QUIC::ssl_quic_tls_index)); + const QUICConnection *qc = static_cast(SSL_get_ex_data(s, QUIC::ssl_quic_qc_index)); + QUICVersion version = qc->negotiated_version(); switch (context) { case SSL_EXT_CLIENT_HELLO: - qtls->set_remote_transport_parameters(std::make_shared(in, inlen)); + qtls->set_remote_transport_parameters(std::make_shared(in, inlen, version)); break; case SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS: - qtls->set_remote_transport_parameters(std::make_shared(in, inlen)); + qtls->set_remote_transport_parameters(std::make_shared(in, inlen, version)); break; default: // Do nothing diff --git a/iocore/net/quic/QUICTransportParameters.h b/iocore/net/quic/QUICTransportParameters.h index d3c19fe4b3d..72533843105 100644 --- a/iocore/net/quic/QUICTransportParameters.h +++ b/iocore/net/quic/QUICTransportParameters.h @@ -34,10 +34,10 @@ class QUICTransportParameterId { public: enum { - ORIGINAL_CONNECTION_ID, + ORIGINAL_DESTINATION_CONNECTION_ID, MAX_IDLE_TIMEOUT, STATELESS_RESET_TOKEN, - MAX_PACKET_SIZE, + MAX_UDP_PAYLOAD_SIZE, INITIAL_MAX_DATA, INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, @@ -49,6 +49,8 @@ class QUICTransportParameterId DISABLE_ACTIVE_MIGRATION, PREFERRED_ADDRESS, ACTIVE_CONNECTION_ID_LIMIT, + INITIAL_SOURCE_CONNECTION_ID, + RETRY_SOURCE_CONNECTION_ID, }; explicit operator bool() const { return true; } @@ -75,7 +77,7 @@ class QUICTransportParameterId class QUICTransportParameters { public: - QUICTransportParameters(const uint8_t *buf, size_t len); + QUICTransportParameters(const uint8_t *buf, size_t len, QUICVersion version); virtual ~QUICTransportParameters(); bool is_valid() const; @@ -104,11 +106,11 @@ class QUICTransportParameters }; QUICTransportParameters(){}; - void _load(const uint8_t *buf, size_t len); + void _load(const uint8_t *buf, size_t len, QUICVersion version); bool _valid = false; virtual std::ptrdiff_t _parameters_offset(const uint8_t *buf) const = 0; - virtual int _validate_parameters() const; + virtual int _validate_parameters(QUICVersion version) const; void _print() const; std::map _parameters; @@ -118,11 +120,11 @@ class QUICTransportParametersInClientHello : public QUICTransportParameters { public: QUICTransportParametersInClientHello() : QUICTransportParameters(){}; - QUICTransportParametersInClientHello(const uint8_t *buf, size_t len); + QUICTransportParametersInClientHello(const uint8_t *buf, size_t len, QUICVersion version); protected: std::ptrdiff_t _parameters_offset(const uint8_t *buf) const override; - int _validate_parameters() const override; + int _validate_parameters(QUICVersion version) const override; private: }; @@ -131,15 +133,11 @@ class QUICTransportParametersInEncryptedExtensions : public QUICTransportParamet { public: QUICTransportParametersInEncryptedExtensions() : QUICTransportParameters(){}; - QUICTransportParametersInEncryptedExtensions(const uint8_t *buf, size_t len); - void add_version(QUICVersion version); + QUICTransportParametersInEncryptedExtensions(const uint8_t *buf, size_t len, QUICVersion version); protected: std::ptrdiff_t _parameters_offset(const uint8_t *buf) const override; - int _validate_parameters() const override; - - uint8_t _n_versions = 0; - QUICVersion _versions[256] = {}; + int _validate_parameters(QUICVersion version) const override; }; class QUICTransportParametersHandler diff --git a/iocore/net/quic/QUICTypes.cc b/iocore/net/quic/QUICTypes.cc index 80a8379d2b3..305abc2b2b7 100644 --- a/iocore/net/quic/QUICTypes.cc +++ b/iocore/net/quic/QUICTypes.cc @@ -153,13 +153,14 @@ QUICTypeUtil::key_phase(QUICPacketType type) QUICPacketNumberSpace QUICTypeUtil::pn_space(QUICEncryptionLevel level) { + // TODO Optimize the case order switch (level) { case QUICEncryptionLevel::HANDSHAKE: - return QUICPacketNumberSpace::Handshake; + return QUICPacketNumberSpace::HANDSHAKE; case QUICEncryptionLevel::INITIAL: - return QUICPacketNumberSpace::Initial; + return QUICPacketNumberSpace::INITIAL; default: - return QUICPacketNumberSpace::ApplicationData; + return QUICPacketNumberSpace::APPLICATION_DATA; } } @@ -360,7 +361,7 @@ QUICResumptionToken::expire_time() const return QUICIntUtil::read_nbytes_as_uint(this->_token + (1 + 20), 4); } -QUICRetryToken::QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid) +QUICRetryToken::QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid, QUICConnectionId scid) { // TODO: read cookie secret from file like SSLTicketKeyConfig static constexpr char stateless_retry_token_secret[] = "stateless_cookie_secret"; @@ -371,8 +372,14 @@ QUICRetryToken::QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_ data_len = strlen(reinterpret_cast(data)); size_t cid_len; + *(data + data_len) = original_dcid.length(); + data_len += 1; QUICTypeUtil::write_QUICConnectionId(original_dcid, data + data_len, &cid_len); data_len += cid_len; + *(data + data_len) = scid.length(); + data_len += 1; + QUICTypeUtil::write_QUICConnectionId(scid, data + data_len, &cid_len); + data_len += cid_len; this->_token[0] = static_cast(Type::RETRY); HMAC(EVP_sha1(), stateless_retry_token_secret, sizeof(stateless_retry_token_secret), data, data_len, this->_token + 1, @@ -380,21 +387,38 @@ QUICRetryToken::QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_ ink_assert(this->_token_len == 20); this->_token_len += 1; + *(this->_token + this->_token_len) = original_dcid.length(); + this->_token_len += 1; QUICTypeUtil::write_QUICConnectionId(original_dcid, this->_token + this->_token_len, &cid_len); this->_token_len += cid_len; + *(this->_token + this->_token_len) = scid.length(); + this->_token_len += 1; + QUICTypeUtil::write_QUICConnectionId(scid, this->_token + this->_token_len, &cid_len); + this->_token_len += cid_len; } bool QUICRetryToken::is_valid(const IpEndpoint &src) const { - return *this == QUICRetryToken(src, this->original_dcid()); + return *this == QUICRetryToken(src, this->original_dcid(), this->scid()); } const QUICConnectionId QUICRetryToken::original_dcid() const { // Type uses 1 byte and output of EVP_sha1() should be 160 bits - return QUICTypeUtil::read_QUICConnectionId(this->_token + (1 + 20), this->_token_len - (1 + 20)); + auto len = *(this->_token + (1 + 20)); + auto start = this->_token + (1 + 20 + 1); + return QUICTypeUtil::read_QUICConnectionId(start, len); +} + +const QUICConnectionId +QUICRetryToken::scid() const +{ + auto len = *(this->_token + (1 + 20)); + auto start = this->_token + (1 + 20 + 1 + len + 1); + len = *(this->_token + (1 + 20 + 1 + len)); + return QUICTypeUtil::read_QUICConnectionId(start, len); } QUICFrameType @@ -674,6 +698,18 @@ QUICConnectionId::hex() const return stream.str(); } +QUICFrameId +QUICSentPacketInfo::FrameInfo::id() const +{ + return this->_id; +} + +QUICFrameGenerator * +QUICSentPacketInfo::FrameInfo::generated_by() const +{ + return this->_generator; +} + // // QUICInvariants // diff --git a/iocore/net/quic/QUICTypes.h b/iocore/net/quic/QUICTypes.h index c93de190309..2f653e985ad 100644 --- a/iocore/net/quic/QUICTypes.h +++ b/iocore/net/quic/QUICTypes.h @@ -44,8 +44,7 @@ using QUICPacketNumber = uint64_t; using QUICVersion = uint32_t; using QUICStreamId = uint64_t; using QUICOffset = uint64_t; - -static constexpr uint8_t kPacketNumberSpace = 3; +using QUICFrameId = uint64_t; // TODO: Update version number // Note: Prefix for drafts (0xff000000) + draft number @@ -53,9 +52,11 @@ static constexpr uint8_t kPacketNumberSpace = 3; // Note: Fix QUIC_ALPN_PROTO_LIST in QUICConfig.cc // Note: Change ExtensionType (QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID) if it's changed constexpr QUICVersion QUIC_SUPPORTED_VERSIONS[] = { + 0xff00001d, 0xff00001b, }; -constexpr QUICVersion QUIC_EXERCISE_VERSION = 0x1a2a3a4a; +constexpr QUICVersion QUIC_EXERCISE_VERSION1 = 0x1a2a3a4a; +constexpr QUICVersion QUIC_EXERCISE_VERSION2 = 0x5a6a7a8a; enum class QUICEncryptionLevel { NONE = -1, @@ -75,13 +76,10 @@ constexpr QUICEncryptionLevel QUIC_ENCRYPTION_LEVELS[] = { QUICEncryptionLevel::ONE_RTT, }; -// introduce by draft-19 kPacketNumberSpace -enum class QUICPacketNumberSpace { - None = -1, - Initial = 0, - Handshake = 1, - ApplicationData = 2, -}; +// kPacketNumberSpace on Recovery A.2.Constants of Interest +enum class QUICPacketNumberSpace : int { INITIAL, HANDSHAKE, APPLICATION_DATA, N_SPACES }; +// For conveniece (this removes neccesity of static_cast) +constexpr int QUIC_N_PACKET_SPACES = static_cast(QUICPacketNumberSpace::N_SPACES); // Divide to QUICPacketType and QUICPacketLongHeaderType ? enum class QUICPacketType : uint8_t { @@ -154,7 +152,7 @@ enum class QUICErrorClass { enum class QUICTransErrorCode : uint64_t { NO_ERROR = 0x00, INTERNAL_ERROR, - SERVER_BUSY, + CONNECTION_REFUSED, FLOW_CONTROL_ERROR, STREAM_LIMIT_ERROR, STREAM_STATE_ERROR, @@ -164,8 +162,9 @@ enum class QUICTransErrorCode : uint64_t { CONNECTION_ID_LIMIT_ERROR, PROTOCOL_VIOLATION, INVALID_TOKEN, - CRYPTO_BUFFER_EXCEEDED = 0x0D, - CRYPTO_ERROR = 0x0100, // 0x100 - 0x1FF + APPLICATION_ERROR, + CRYPTO_BUFFER_EXCEEDED, + CRYPTO_ERROR = 0x0100, // 0x100 - 0x1FF }; // Application Protocol Error Codes defined in application @@ -381,7 +380,7 @@ class QUICRetryToken : public QUICAddressValidationToken { public: QUICRetryToken(const uint8_t *buf, size_t len) : QUICAddressValidationToken(buf, len) {} - QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid); + QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid, QUICConnectionId scid); bool operator==(const QUICRetryToken &x) const @@ -395,9 +394,7 @@ class QUICRetryToken : public QUICAddressValidationToken bool is_valid(const IpEndpoint &src) const; const QUICConnectionId original_dcid() const; - -private: - QUICConnectionId _original_dcid; + const QUICConnectionId scid() const; }; class QUICPreferredAddress @@ -551,13 +548,54 @@ class QUICCCConfig { public: virtual ~QUICCCConfig() {} - virtual uint32_t max_datagram_size() const = 0; virtual uint32_t initial_window() const = 0; virtual uint32_t minimum_window() const = 0; virtual float loss_reduction_factor() const = 0; virtual uint32_t persistent_congestion_threshold() const = 0; }; +class QUICFrameGenerator; + +struct QUICSentPacketInfo { + class FrameInfo + { + public: + FrameInfo(QUICFrameId id, QUICFrameGenerator *generator) : _id(id), _generator(generator) {} + + QUICFrameId id() const; + QUICFrameGenerator *generated_by() const; + + private: + QUICFrameId _id = 0; + QUICFrameGenerator *_generator; + }; + + // Recovery A.1.1. Sent Packet Fields + QUICPacketNumber packet_number; + bool ack_eliciting; + bool in_flight; + size_t sent_bytes; + ink_hrtime time_sent; + + // Additional fields + QUICPacketType type; + std::vector frames; + QUICPacketNumberSpace pn_space; + // End of additional fields +}; + +using QUICSentPacketInfoUPtr = std::unique_ptr; + +class QUICRTTProvider +{ +public: + virtual ink_hrtime smoothed_rtt() const = 0; + virtual ink_hrtime rttvar() const = 0; + virtual ink_hrtime latest_rtt() const = 0; + + virtual ink_hrtime congestion_period(uint32_t threshold) const = 0; +}; + // TODO: move version independent functions to QUICInvariants class QUICTypeUtil { diff --git a/iocore/net/quic/qlog/QLogListener.h b/iocore/net/quic/qlog/QLogListener.h index 7b55af2ceff..9337b075f5d 100644 --- a/iocore/net/quic/qlog/QLogListener.h +++ b/iocore/net/quic/qlog/QLogListener.h @@ -73,7 +73,7 @@ class QLogListener : public QUICCallback }; void - packet_lost_callback(QUICCallbackContext &, const QUICPacketInfo &packet) override + packet_lost_callback(QUICCallbackContext &, const QUICSentPacketInfo &packet) override { auto qe = std::make_unique(PacketTypeToName(packet.type), packet.packet_number); this->_log.last_trace().push_event(std::move(qe)); diff --git a/iocore/net/quic/test/test_QUICAckFrameCreator.cc b/iocore/net/quic/test/test_QUICAckFrameCreator.cc index 2b66f061f72..eb96869c90f 100644 --- a/iocore/net/quic/test/test_QUICAckFrameCreator.cc +++ b/iocore/net/quic/test/test_QUICAckFrameCreator.cc @@ -283,7 +283,7 @@ TEST_CASE("QUICAckFrameManager_loss_recover", "[quic]") TEST_CASE("QUICAckFrameManager_QUICAckFrameCreator", "[quic]") { QUICAckFrameManager ack_manager; - QUICAckFrameManager::QUICAckFrameCreator packet_numbers(QUICPacketNumberSpace::Initial, &ack_manager); + QUICAckFrameManager::QUICAckFrameCreator packet_numbers(QUICPacketNumberSpace::INITIAL, &ack_manager); CHECK(packet_numbers.size() == 0); CHECK(packet_numbers.largest_ack_number() == 0); diff --git a/iocore/net/quic/test/test_QUICHandshakeProtocol.cc b/iocore/net/quic/test/test_QUICHandshakeProtocol.cc index 4a71a07be96..e69786810d9 100644 --- a/iocore/net/quic/test/test_QUICHandshakeProtocol.cc +++ b/iocore/net/quic/test/test_QUICHandshakeProtocol.cc @@ -33,11 +33,12 @@ #include -// #include "Mock.h" #include "QUICPacketHeaderProtector.h" #include "QUICPacketPayloadProtector.h" #include "QUICPacketProtectionKeyInfo.h" #include "QUICTLS.h" +#include "QUICGlobals.h" +#include "Mock.h" // XXX For NetVCOptions::reset struct PollCont; @@ -107,8 +108,12 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketProtectionKeyInfo pp_key_info_client; QUICPacketProtectionKeyInfo pp_key_info_server; NetVCOptions netvc_options; + MockQUICConnection mock_client_connection; + MockQUICConnection mock_server_connection; QUICHandshakeProtocol *client = new QUICTLS(pp_key_info_client, client_ssl_ctx, NET_VCONNECTION_OUT, netvc_options); QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); + SSL_set_ex_data(static_cast(client)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_client_connection); + SSL_set_ex_data(static_cast(server)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_server_connection); QUICPacketPayloadProtector ppp_client(pp_key_info_client); QUICPacketPayloadProtector ppp_server(pp_key_info_server); @@ -119,8 +124,10 @@ TEST_CASE("QUICHandshakeProtocol") client->set_local_transport_parameters(client_tp); server->set_local_transport_parameters(server_tp); - CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); - CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); + CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); // CH QUICHandshakeMsgs msg0; @@ -232,8 +239,12 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketProtectionKeyInfo pp_key_info_client; QUICPacketProtectionKeyInfo pp_key_info_server; NetVCOptions netvc_options; + MockQUICConnection mock_client_connection; + MockQUICConnection mock_server_connection; QUICHandshakeProtocol *client = new QUICTLS(pp_key_info_client, client_ssl_ctx, NET_VCONNECTION_OUT, netvc_options); QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); + SSL_set_ex_data(static_cast(client)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_client_connection); + SSL_set_ex_data(static_cast(server)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_server_connection); QUICPacketPayloadProtector ppp_client(pp_key_info_client); QUICPacketPayloadProtector ppp_server(pp_key_info_server); @@ -244,8 +255,10 @@ TEST_CASE("QUICHandshakeProtocol") client->set_local_transport_parameters(client_tp); server->set_local_transport_parameters(server_tp); - CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); - CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); + CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); // CH QUICHandshakeMsgs msg0; @@ -369,7 +382,8 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketProtectionKeyInfo pp_key_info_server; NetVCOptions netvc_options; QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); - CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); + CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); // Malformed CH (finished) uint8_t msg1_buf[] = {0x14, 0x00, 0x00, 0x30, 0x35, 0xb9, 0x82, 0x9d, 0xb9, 0x14, 0x70, 0x03, 0x60, @@ -401,8 +415,12 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketProtectionKeyInfo pp_key_info_client; QUICPacketProtectionKeyInfo pp_key_info_server; NetVCOptions netvc_options; + MockQUICConnection mock_client_connection; + MockQUICConnection mock_server_connection; QUICHandshakeProtocol *client = new QUICTLS(pp_key_info_client, client_ssl_ctx, NET_VCONNECTION_OUT, netvc_options); QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); + SSL_set_ex_data(static_cast(client)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_client_connection); + SSL_set_ex_data(static_cast(server)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_server_connection); auto client_tp = std::make_shared(); auto server_tp = std::make_shared(); @@ -411,8 +429,10 @@ TEST_CASE("QUICHandshakeProtocol") client->set_local_transport_parameters(client_tp); server->set_local_transport_parameters(server_tp); - CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); - CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); + CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); // # Start Handshake diff --git a/iocore/net/quic/test/test_QUICKeyGenerator.cc b/iocore/net/quic/test/test_QUICKeyGenerator.cc index 4c1858a1562..cd9ff38f22e 100644 --- a/iocore/net/quic/test/test_QUICKeyGenerator.cc +++ b/iocore/net/quic/test/test_QUICKeyGenerator.cc @@ -35,8 +35,8 @@ #include "QUICKeyGenerator.h" #include "QUICPacketProtectionKeyInfo.h" -// https://github.com/quicwg/base-drafts/wiki/Test-Vector-for-the-Clear-Text-AEAD-key-derivation -TEST_CASE("draft-23 Test Vectors", "[quic]") +// https://github.com/quicwg/base-drafts/wiki/Test-Vector-for-the-Initial-AEAD-key-derivation +TEST_CASE("draft-23~27 Test Vectors", "[quic]") { SECTION("CLIENT Initial") { @@ -51,8 +51,9 @@ TEST_CASE("draft-23 Test Vectors", "[quic]") QUICPacketProtectionKeyInfo pp_key_info; pp_key_info.set_cipher_initial(EVP_aes_128_gcm()); pp_key_info.set_cipher_for_hp_initial(EVP_aes_128_ecb()); - keygen.generate(pp_key_info.encryption_key_for_hp(QUICKeyPhase::INITIAL), pp_key_info.encryption_key(QUICKeyPhase::INITIAL), - pp_key_info.encryption_iv(QUICKeyPhase::INITIAL), pp_key_info.encryption_iv_len(QUICKeyPhase::INITIAL), cid); + keygen.generate(0xff00001b, pp_key_info.encryption_key_for_hp(QUICKeyPhase::INITIAL), + pp_key_info.encryption_key(QUICKeyPhase::INITIAL), pp_key_info.encryption_iv(QUICKeyPhase::INITIAL), + pp_key_info.encryption_iv_len(QUICKeyPhase::INITIAL), cid); CHECK(pp_key_info.encryption_key_len(QUICKeyPhase::INITIAL) == sizeof(expected_client_key)); CHECK(memcmp(pp_key_info.encryption_key(QUICKeyPhase::INITIAL), expected_client_key, sizeof(expected_client_key)) == 0); @@ -76,8 +77,9 @@ TEST_CASE("draft-23 Test Vectors", "[quic]") QUICPacketProtectionKeyInfo pp_key_info; pp_key_info.set_cipher_initial(EVP_aes_128_gcm()); pp_key_info.set_cipher_for_hp_initial(EVP_aes_128_ecb()); - keygen.generate(pp_key_info.encryption_key_for_hp(QUICKeyPhase::INITIAL), pp_key_info.encryption_key(QUICKeyPhase::INITIAL), - pp_key_info.encryption_iv(QUICKeyPhase::INITIAL), pp_key_info.encryption_iv_len(QUICKeyPhase::INITIAL), cid); + keygen.generate(0xff00001b, pp_key_info.encryption_key_for_hp(QUICKeyPhase::INITIAL), + pp_key_info.encryption_key(QUICKeyPhase::INITIAL), pp_key_info.encryption_iv(QUICKeyPhase::INITIAL), + pp_key_info.encryption_iv_len(QUICKeyPhase::INITIAL), cid); CHECK(pp_key_info.encryption_key_len(QUICKeyPhase::INITIAL) == sizeof(expected_server_key)); CHECK(memcmp(pp_key_info.encryption_key(QUICKeyPhase::INITIAL), expected_server_key, sizeof(expected_server_key)) == 0); diff --git a/iocore/net/quic/test/test_QUICLossDetector.cc b/iocore/net/quic/test/test_QUICLossDetector.cc index acabd8252ad..5dd827143eb 100644 --- a/iocore/net/quic/test/test_QUICLossDetector.cc +++ b/iocore/net/quic/test/test_QUICLossDetector.cc @@ -27,6 +27,8 @@ #include "QUICPacketFactory.h" #include "QUICAckFrameCreator.h" #include "QUICEvents.h" +#include "QUICPacketFactory.h" +#include "QUICAckFrameCreator.h" #include "Mock.h" #include "tscore/ink_hrtime.h" @@ -79,16 +81,15 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, sizeof(raw), 0, true, true, false); handshake_packet->attach_payload(payload, true); QUICPacketUPtr packet = QUICPacketUPtr(handshake_packet, [](QUICPacket *p) { delete p; }); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{ + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{ packet->packet_number(), - Thread::get_hrtime(), packet->is_ack_eliciting(), - static_cast(*packet).is_crypto_packet(), true, packet->size(), + Thread::get_hrtime(), packet->type(), {}, - QUICPacketNumberSpace::Handshake, + QUICPacketNumberSpace::HANDSHAKE, })); ink_hrtime_sleep(HRTIME_MSECONDS(1000)); CHECK(g.lost_frame_count >= 0); @@ -106,7 +107,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") SECTION("1-RTT") { // Send packet (1) to (7) - QUICPacketNumberSpace pn_space = QUICPacketNumberSpace::ApplicationData; + QUICPacketNumberSpace pn_space = QUICPacketNumberSpace::APPLICATION_DATA; QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; payload = make_ptr(new_IOBufferBlock()); payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); @@ -181,97 +182,87 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") QUICPacketNumber pn9 = packet9->packet_number(); QUICPacketNumber pn10 = packet10->packet_number(); - QUICPacketInfoUPtr packet_info = nullptr; - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet1->packet_number(), - Thread::get_hrtime(), - packet1->is_ack_eliciting(), - false, - true, - packet1->size(), - packet1->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet2->packet_number(), - Thread::get_hrtime(), - packet2->is_ack_eliciting(), - false, - true, - packet2->size(), - packet2->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet3->packet_number(), - Thread::get_hrtime(), - packet3->is_ack_eliciting(), - false, - true, - packet3->size(), - packet3->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet4->packet_number(), - Thread::get_hrtime(), - packet4->is_ack_eliciting(), - false, - true, - packet4->size(), - packet4->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet5->packet_number(), - Thread::get_hrtime(), - packet5->is_ack_eliciting(), - false, - true, - packet5->size(), - packet5->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet6->packet_number(), - Thread::get_hrtime(), - packet6->is_ack_eliciting(), - false, - true, - packet6->size(), - packet6->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet7->packet_number(), - Thread::get_hrtime(), - packet6->is_ack_eliciting(), - false, - true, - packet7->size(), - packet7->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet8->packet_number(), - Thread::get_hrtime(), - packet6->is_ack_eliciting(), - false, - true, - packet8->size(), - packet8->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet9->packet_number(), - Thread::get_hrtime(), - packet6->is_ack_eliciting(), - false, - true, - packet9->size(), - packet9->type(), - {}, - pn_space})); - detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet10->packet_number(), - Thread::get_hrtime(), - packet10->is_ack_eliciting(), - false, - true, - packet10->size(), - packet10->type(), - {}, - pn_space})); + QUICSentPacketInfoUPtr packet_info = nullptr; + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet1->packet_number(), + packet1->is_ack_eliciting(), + true, + packet1->size(), + Thread::get_hrtime(), + packet1->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet2->packet_number(), + packet2->is_ack_eliciting(), + true, + packet2->size(), + Thread::get_hrtime(), + packet2->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet3->packet_number(), + packet3->is_ack_eliciting(), + true, + packet3->size(), + Thread::get_hrtime(), + packet3->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet4->packet_number(), + packet4->is_ack_eliciting(), + true, + packet4->size(), + Thread::get_hrtime(), + packet4->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet5->packet_number(), + packet5->is_ack_eliciting(), + true, + packet5->size(), + Thread::get_hrtime(), + packet5->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet6->packet_number(), + packet6->is_ack_eliciting(), + true, + packet6->size(), + Thread::get_hrtime(), + packet6->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet7->packet_number(), + packet6->is_ack_eliciting(), + true, + packet7->size(), + Thread::get_hrtime(), + packet7->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet8->packet_number(), + packet6->is_ack_eliciting(), + true, + packet8->size(), + Thread::get_hrtime(), + packet8->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet9->packet_number(), + packet6->is_ack_eliciting(), + true, + packet9->size(), + Thread::get_hrtime(), + packet9->type(), + {}, + pn_space})); + detector.on_packet_sent(QUICSentPacketInfoUPtr(new QUICSentPacketInfo{packet10->packet_number(), + packet10->is_ack_eliciting(), + true, + packet10->size(), + Thread::get_hrtime(), + packet10->type(), + {}, + pn_space})); ink_hrtime_sleep(HRTIME_MSECONDS(2000)); // Receive an ACK for (1) (4) (5) (7) (8) (9) diff --git a/iocore/net/quic/test/test_QUICPacket.cc b/iocore/net/quic/test/test_QUICPacket.cc index 5dda30a6016..35ffd5ea43c 100644 --- a/iocore/net/quic/test/test_QUICPacket.cc +++ b/iocore/net/quic/test/test_QUICPacket.cc @@ -44,7 +44,7 @@ TEST_CASE("Receiving Packet", "[quic]") 0x08, // SCID Len 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID 0x00, 0x00, 0x00, 0x08, // Supported Version 1 - 0x00, 0x00, 0x00, 0x09, // Supported Version 1 + 0x00, 0x00, 0x00, 0x09, // Supported Version 2 }; Ptr input_ibb = make_ptr(new_IOBufferBlock()); input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); @@ -255,8 +255,8 @@ TEST_CASE("Sending Packet", "[quic]") SECTION("RETRY Packet (store)") { - uint8_t buf[64] = {0}; - size_t len = 0; + uint8_t buf[128] = {0}; + size_t len = 0; const uint8_t expected[] = { 0xf0, // Long header, Type: RETRY @@ -267,11 +267,13 @@ TEST_CASE("Sending Packet", "[quic]") 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Retry Token 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // - 0x10, 0x11, 0x12, 0x13, 0x14, 0xf0, 0xf1, 0xf2, // + 0x10, 0x11, 0x12, 0x13, 0x14, 0x08, 0x01, 0x02, // + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x08, 0x11, // + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Retry Integrity Tag 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - QUICRetryToken token(expected + 23, 24); + QUICRetryToken token(expected + 23, 39); QUICRetryPacket packet(0x11223344, {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, token); @@ -285,6 +287,21 @@ TEST_CASE("Sending Packet", "[quic]") CHECK(len == packet.size()); CHECK(memcmp(buf, expected, sizeof(expected) - 16) == 0); } + + SECTION("VersionNegotiation Packet") + { + QUICConnectionId dummy; + QUICVersionNegotiationPacket vn1(dummy, dummy, QUIC_SUPPORTED_VERSIONS, countof(QUIC_SUPPORTED_VERSIONS), + QUIC_EXERCISE_VERSION1); + for (auto i = 0; i < vn1.nversions(); ++i) { + REQUIRE(vn1.versions()[i] != QUIC_EXERCISE_VERSION1); + } + QUICVersionNegotiationPacket vn2(dummy, dummy, QUIC_SUPPORTED_VERSIONS, countof(QUIC_SUPPORTED_VERSIONS), + QUIC_EXERCISE_VERSION2); + for (auto i = 0; i < vn2.nversions(); ++i) { + REQUIRE(vn2.versions()[i] != QUIC_EXERCISE_VERSION2); + } + } } TEST_CASE("Encoded Packet Number Length", "[quic]") diff --git a/iocore/net/quic/test/test_QUICPacketFactory.cc b/iocore/net/quic/test/test_QUICPacketFactory.cc index 0df343660fa..57bf6b34885 100644 --- a/iocore/net/quic/test/test_QUICPacketFactory.cc +++ b/iocore/net/quic/test/test_QUICPacketFactory.cc @@ -37,7 +37,7 @@ TEST_CASE("QUICPacketFactory_Create_VersionNegotiationPacket", "[quic]") QUICConnectionId dcid(raw_dcid, 8); QUICConnectionId scid(raw_scid, 8); - QUICPacketUPtr packet = factory.create_version_negotiation_packet(scid, dcid); + QUICPacketUPtr packet = factory.create_version_negotiation_packet(scid, dcid, QUIC_EXERCISE_VERSION1); REQUIRE(packet != nullptr); QUICVersionNegotiationPacket &vn_packet = static_cast(*packet); @@ -56,8 +56,9 @@ TEST_CASE("QUICPacketFactory_Create_VersionNegotiationPacket", "[quic]") 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Destination Connection ID 0x08, // SCID Len 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Source Connection ID + 0xff, 0x00, 0x00, 0x1d, // Supported Version 0xff, 0x00, 0x00, 0x1b, // Supported Version - 0x1a, 0x2a, 0x3a, 0x4a, // Exercise Version + 0x5a, 0x6a, 0x7a, 0x8a, // Exercise Version }; uint8_t buf[1024] = {0}; size_t buf_len; @@ -75,9 +76,9 @@ TEST_CASE("QUICPacketFactory_Create_Retry", "[quic]") uint8_t raw[] = {0xaa, 0xbb, 0xcc, 0xdd}; QUICRetryToken token(raw, 4); - QUICPacketUPtr packet = - factory.create_retry_packet(QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), - QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), token); + QUICPacketUPtr packet = factory.create_retry_packet( + QUIC_SUPPORTED_VERSIONS[0], QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), + QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), token); REQUIRE(packet != nullptr); diff --git a/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc b/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc index 031aa51cd37..06255956109 100644 --- a/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc +++ b/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc @@ -26,6 +26,8 @@ #include "QUICPacketProtectionKeyInfo.h" #include "QUICPacketHeaderProtector.h" #include "QUICTLS.h" +#include "QUICGlobals.h" +#include "Mock.h" struct PollCont; #include "P_UDPConnection.h" @@ -87,8 +89,10 @@ TEST_CASE("QUICPacketHeaderProtector") QUICHandshakeProtocol *client = new QUICTLS(pp_key_info_client, client_ssl_ctx, NET_VCONNECTION_OUT, netvc_options); QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); - CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); - CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); + CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); QUICPacketHeaderProtector client_ph_protector(pp_key_info_client); QUICPacketHeaderProtector server_ph_protector(pp_key_info_server); @@ -121,8 +125,12 @@ TEST_CASE("QUICPacketHeaderProtector") QUICPacketProtectionKeyInfo pp_key_info_client; QUICPacketProtectionKeyInfo pp_key_info_server; NetVCOptions netvc_options; + MockQUICConnection mock_client_connection; + MockQUICConnection mock_server_connection; QUICHandshakeProtocol *client = new QUICTLS(pp_key_info_client, client_ssl_ctx, NET_VCONNECTION_OUT, netvc_options); QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); + SSL_set_ex_data(static_cast(client)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_client_connection); + SSL_set_ex_data(static_cast(server)->ssl_handle(), QUIC::ssl_quic_qc_index, &mock_server_connection); auto client_tp = std::make_shared(); auto server_tp = std::make_shared(); @@ -131,8 +139,10 @@ TEST_CASE("QUICPacketHeaderProtector") client->set_local_transport_parameters(client_tp); server->set_local_transport_parameters(server_tp); - CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); - CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); + CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8}, + QUIC_SUPPORTED_VERSIONS[0])); QUICPacketHeaderProtector client_ph_protector(pp_key_info_client); QUICPacketHeaderProtector server_ph_protector(pp_key_info_server); diff --git a/iocore/net/quic/test/test_QUICStreamManager.cc b/iocore/net/quic/test/test_QUICStreamManager.cc index 8d7ddf28bfc..ad9443deb54 100644 --- a/iocore/net/quic/test/test_QUICStreamManager.cc +++ b/iocore/net/quic/test/test_QUICStreamManager.cc @@ -47,7 +47,7 @@ TEST_CASE("QUICStreamManager_NewStream", "[quic]") 0x40, 0x10 // value }; std::shared_ptr local_tp = - std::make_shared(local_tp_buf, sizeof(local_tp_buf)); + std::make_shared(local_tp_buf, sizeof(local_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); uint8_t remote_tp_buf[] = { 0x08, // parameter id - initial_max_streams_bidi @@ -55,7 +55,7 @@ TEST_CASE("QUICStreamManager_NewStream", "[quic]") 0x40, 0x10 // value }; std::shared_ptr remote_tp = - std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); + std::make_shared(remote_tp_buf, sizeof(remote_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); sm.init_flow_control_params(local_tp, remote_tp); @@ -139,7 +139,7 @@ TEST_CASE("QUICStreamManager_total_offset_received", "[quic]") 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr local_tp = - std::make_shared(local_tp_buf, sizeof(local_tp_buf)); + std::make_shared(local_tp_buf, sizeof(local_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); uint8_t remote_tp_buf[] = { 0x08, // parameter id - initial_max_streams_bidi @@ -150,7 +150,7 @@ TEST_CASE("QUICStreamManager_total_offset_received", "[quic]") 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr remote_tp = - std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); + std::make_shared(remote_tp_buf, sizeof(remote_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); sm.init_flow_control_params(local_tp, remote_tp); @@ -194,7 +194,7 @@ TEST_CASE("QUICStreamManager_total_offset_sent", "[quic]") 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr local_tp = - std::make_shared(local_tp_buf, sizeof(local_tp_buf)); + std::make_shared(local_tp_buf, sizeof(local_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); uint8_t remote_tp_buf[] = { 0x08, // parameter id - initial_max_streams_bidi @@ -205,7 +205,7 @@ TEST_CASE("QUICStreamManager_total_offset_sent", "[quic]") 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr remote_tp = - std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); + std::make_shared(remote_tp_buf, sizeof(remote_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); sm.init_flow_control_params(local_tp, remote_tp); @@ -262,7 +262,7 @@ TEST_CASE("QUICStreamManager_max_streams", "[quic]") 0x03, // value }; std::shared_ptr local_tp = - std::make_shared(local_tp_buf, sizeof(local_tp_buf)); + std::make_shared(local_tp_buf, sizeof(local_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); uint8_t remote_tp_buf[] = { 0x08, // parameter id - initial_max_streams_bidi @@ -273,7 +273,7 @@ TEST_CASE("QUICStreamManager_max_streams", "[quic]") 0x03, // value }; std::shared_ptr remote_tp = - std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); + std::make_shared(remote_tp_buf, sizeof(remote_tp_buf), QUIC_SUPPORTED_VERSIONS[0]); sm.init_flow_control_params(local_tp, remote_tp); diff --git a/iocore/net/quic/test/test_QUICTransportParameters.cc b/iocore/net/quic/test/test_QUICTransportParameters.cc index a225b86fed7..cddad460f8a 100644 --- a/iocore/net/quic/test/test_QUICTransportParameters.cc +++ b/iocore/net/quic/test/test_QUICTransportParameters.cc @@ -44,13 +44,13 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") 0x05, 0x67, // value }; - QUICTransportParametersInClientHello params_in_ch(buf, sizeof(buf)); + QUICTransportParametersInClientHello params_in_ch(buf, sizeof(buf), QUIC_SUPPORTED_VERSIONS[0]); CHECK(params_in_ch.is_valid()); uint16_t len = 0; const uint8_t *data = nullptr; - data = params_in_ch.getAsBytes(QUICTransportParameterId::ORIGINAL_CONNECTION_ID, len); + data = params_in_ch.getAsBytes(QUICTransportParameterId::ORIGINAL_DESTINATION_CONNECTION_ID, len); CHECK(len == 4); CHECK(memcmp(data, "\x11\x22\x33\x44", 4) == 0); @@ -62,7 +62,7 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") CHECK(len == 2); CHECK(memcmp(data, "\x0a\x0b", 2) == 0); - data = params_in_ch.getAsBytes(QUICTransportParameterId::MAX_PACKET_SIZE, len); + data = params_in_ch.getAsBytes(QUICTransportParameterId::MAX_UDP_PAYLOAD_SIZE, len); CHECK(len == 2); CHECK(memcmp(data, "\x05\x67", 2) == 0); @@ -82,7 +82,7 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") 0x12, 0x34, 0x56, 0x78, // value }; - QUICTransportParametersInClientHello params_in_ch(buf, sizeof(buf)); + QUICTransportParametersInClientHello params_in_ch(buf, sizeof(buf), QUIC_SUPPORTED_VERSIONS[0]); CHECK(!params_in_ch.is_valid()); } } @@ -111,7 +111,7 @@ TEST_CASE("QUICTransportParametersInClientHello_write", "[quic]") params_in_ch.set(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, max_stream_data); uint16_t max_packet_size = 0x1bcd; - params_in_ch.set(QUICTransportParameterId::MAX_PACKET_SIZE, max_packet_size); + params_in_ch.set(QUICTransportParameterId::MAX_UDP_PAYLOAD_SIZE, max_packet_size); uint8_t stateless_reset_token[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; @@ -142,7 +142,7 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") 0x91, 0x22, 0x33, 0x44, // value }; - QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf)); + QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf), QUIC_SUPPORTED_VERSIONS[0]); CHECK(params_in_ee.is_valid()); uint16_t len = 0; @@ -183,7 +183,7 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") 0x00, // length of value }; - QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf)); + QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf), QUIC_SUPPORTED_VERSIONS[0]); CHECK(params_in_ee.is_valid()); uint16_t len = 0; @@ -215,7 +215,7 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") 0x12, 0x34, 0x56, 0x78, // value }; - QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf)); + QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf), QUIC_SUPPORTED_VERSIONS[0]); CHECK(!params_in_ee.is_valid()); } } @@ -242,10 +242,8 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") params_in_ee.set(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, max_stream_data); uint16_t max_packet_size = 0x1bcd; - params_in_ee.set(QUICTransportParameterId::MAX_PACKET_SIZE, max_packet_size); + params_in_ee.set(QUICTransportParameterId::MAX_UDP_PAYLOAD_SIZE, max_packet_size); - params_in_ee.add_version(0x01020304); - params_in_ee.add_version(0x05060708); params_in_ee.store(buf, &len); CHECK(len == 10); CHECK(memcmp(buf, expected, len) == 0); @@ -273,11 +271,9 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") params_in_ee.set(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, max_stream_data); uint16_t max_packet_size = 0x1bcd; - params_in_ee.set(QUICTransportParameterId::MAX_PACKET_SIZE, max_packet_size); + params_in_ee.set(QUICTransportParameterId::MAX_UDP_PAYLOAD_SIZE, max_packet_size); params_in_ee.set(QUICTransportParameterId::DISABLE_ACTIVE_MIGRATION, nullptr, 0); - params_in_ee.add_version(0x01020304); - params_in_ee.add_version(0x05060708); params_in_ee.store(buf, &len); CHECK(len == 12); CHECK(memcmp(buf, expected, len) == 0); diff --git a/iocore/net/quic/test/test_QUICType.cc b/iocore/net/quic/test/test_QUICType.cc index fb45c44c05d..b08085cf818 100644 --- a/iocore/net/quic/test/test_QUICType.cc +++ b/iocore/net/quic/test/test_QUICType.cc @@ -103,11 +103,14 @@ TEST_CASE("QUICType", "[quic]") IpEndpoint ep; ats_ip4_set(&ep, 0x04030201, 0x2211); - uint8_t cid_buf[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27}; - QUICConnectionId cid(cid_buf, sizeof(cid_buf)); - - QUICRetryToken token1(ep, cid); + uint8_t cid1_buf[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27}; + QUICConnectionId cid1(cid1_buf, sizeof(cid1_buf)); + uint8_t cid2_buf[] = {0xA0, 0xA1, 0x12, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}; + QUICConnectionId cid2(cid2_buf, sizeof(cid2_buf)); + + QUICRetryToken token1(ep, cid1, cid2); QUICRetryToken token2(token1.buf(), token1.length()); CHECK(token1.is_valid(ep)); @@ -118,6 +121,7 @@ TEST_CASE("QUICType", "[quic]") CHECK(token1.length() == token2.length()); CHECK(memcmp(token1.buf(), token2.buf(), token1.length()) == 0); CHECK(token1.original_dcid() == token2.original_dcid()); + CHECK(token1.scid() == token2.scid()); } SECTION("QUICResumptionToken") diff --git a/iocore/net/quic/test/test_QUICVersionNegotiator.cc b/iocore/net/quic/test/test_QUICVersionNegotiator.cc index a1bcab3f3b0..35397459c86 100644 --- a/iocore/net/quic/test/test_QUICVersionNegotiator.cc +++ b/iocore/net/quic/test/test_QUICVersionNegotiator.cc @@ -24,6 +24,7 @@ #include "catch.hpp" #include "quic/QUICVersionNegotiator.h" +#include "quic/QUICPacketFactory.h" #include "quic/QUICPacketProtectionKeyInfo.h" #include "quic/QUICPacketFactory.h" #include "quic/Mock.h" @@ -41,7 +42,7 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") dummy_payload->alloc(iobuffer_size_to_index(dummy_payload_len, BUFFER_SIZE_INDEX_32K)); dummy_payload->fill(dummy_payload_len); - SECTION("Normal case") + SECTION("Match") { // Check initial state CHECK(vn.status() == QUICVersionNegotiationStatus::NOT_NEGOTIATED); @@ -60,13 +61,13 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); } - SECTION("Negotiation case") + SECTION("Unmatch") { // Check initial state CHECK(vn.status() == QUICVersionNegotiationStatus::NOT_NEGOTIATED); // Negotiate version - packet_factory.set_version(QUIC_SUPPORTED_VERSIONS[0]); + packet_factory.set_version(0xff000001); uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = packet_factory.create_initial_packet(packet_buf, {}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); @@ -76,16 +77,16 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") blocks->next = initial_packet->payload_block(); QUICInitialPacketR received_initial_packet(nullptr, {}, {}, blocks, 0); vn.negotiate(received_initial_packet); - CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); + CHECK(vn.status() == QUICVersionNegotiationStatus::NOT_NEGOTIATED); } - SECTION("Downgrade case") + SECTION("Exercise") { // Check initial state CHECK(vn.status() == QUICVersionNegotiationStatus::NOT_NEGOTIATED); // Negotiate version - packet_factory.set_version(QUIC_EXERCISE_VERSION); + packet_factory.set_version(QUIC_EXERCISE_VERSION1); uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = packet_factory.create_initial_packet(packet_buf, {}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); @@ -125,7 +126,7 @@ TEST_CASE("QUICVersionNegotiator - Client Side", "[quic]") CHECK(vn.status() == QUICVersionNegotiationStatus::NOT_NEGOTIATED); // Negotiate version - packet_factory.set_version(QUIC_EXERCISE_VERSION); + packet_factory.set_version(QUIC_EXERCISE_VERSION1); uint8_t initial_packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr packet = packet_factory.create_initial_packet(initial_packet_buf, {}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); @@ -133,8 +134,8 @@ TEST_CASE("QUICVersionNegotiator - Client Side", "[quic]") QUICInitialPacket &initial_packet = static_cast(*packet); // Server send VN packet based on Initial packet - QUICPacketUPtr vn_packet = - packet_factory.create_version_negotiation_packet(initial_packet.source_cid(), initial_packet.destination_cid()); + QUICPacketUPtr vn_packet = packet_factory.create_version_negotiation_packet( + initial_packet.source_cid(), initial_packet.destination_cid(), QUIC_EXERCISE_VERSION1); REQUIRE(vn_packet != nullptr); auto blocks = vn_packet->header_block(); diff --git a/lib/records/RecHttp.cc b/lib/records/RecHttp.cc index 351792db70e..00afaabd160 100644 --- a/lib/records/RecHttp.cc +++ b/lib/records/RecHttp.cc @@ -38,39 +38,45 @@ SessionProtocolNameRegistry globalSessionProtocolNameRegistry; These are also used for NPN setup. */ -const char *const TS_ALPN_PROTOCOL_HTTP_0_9 = IP_PROTO_TAG_HTTP_0_9.data(); -const char *const TS_ALPN_PROTOCOL_HTTP_1_0 = IP_PROTO_TAG_HTTP_1_0.data(); -const char *const TS_ALPN_PROTOCOL_HTTP_1_1 = IP_PROTO_TAG_HTTP_1_1.data(); -const char *const TS_ALPN_PROTOCOL_HTTP_2_0 = IP_PROTO_TAG_HTTP_2_0.data(); -const char *const TS_ALPN_PROTOCOL_HTTP_3 = IP_PROTO_TAG_HTTP_3.data(); -const char *const TS_ALPN_PROTOCOL_HTTP_QUIC = IP_PROTO_TAG_HTTP_QUIC.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_0_9 = IP_PROTO_TAG_HTTP_0_9.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_1_0 = IP_PROTO_TAG_HTTP_1_0.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_1_1 = IP_PROTO_TAG_HTTP_1_1.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_2_0 = IP_PROTO_TAG_HTTP_2_0.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_3 = IP_PROTO_TAG_HTTP_3.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_QUIC = IP_PROTO_TAG_HTTP_QUIC.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_3_D27 = IP_PROTO_TAG_HTTP_3_D27.data(); +const char *const TS_ALPN_PROTOCOL_HTTP_QUIC_D27 = IP_PROTO_TAG_HTTP_QUIC_D27.data(); const char *const TS_ALPN_PROTOCOL_GROUP_HTTP = "http"; const char *const TS_ALPN_PROTOCOL_GROUP_HTTP2 = "http2"; -const char *const TS_PROTO_TAG_HTTP_1_0 = TS_ALPN_PROTOCOL_HTTP_1_0; -const char *const TS_PROTO_TAG_HTTP_1_1 = TS_ALPN_PROTOCOL_HTTP_1_1; -const char *const TS_PROTO_TAG_HTTP_2_0 = TS_ALPN_PROTOCOL_HTTP_2_0; -const char *const TS_PROTO_TAG_HTTP_3 = TS_ALPN_PROTOCOL_HTTP_3; -const char *const TS_PROTO_TAG_HTTP_QUIC = TS_ALPN_PROTOCOL_HTTP_QUIC; -const char *const TS_PROTO_TAG_TLS_1_3 = IP_PROTO_TAG_TLS_1_3.data(); -const char *const TS_PROTO_TAG_TLS_1_2 = IP_PROTO_TAG_TLS_1_2.data(); -const char *const TS_PROTO_TAG_TLS_1_1 = IP_PROTO_TAG_TLS_1_1.data(); -const char *const TS_PROTO_TAG_TLS_1_0 = IP_PROTO_TAG_TLS_1_0.data(); -const char *const TS_PROTO_TAG_TCP = IP_PROTO_TAG_TCP.data(); -const char *const TS_PROTO_TAG_UDP = IP_PROTO_TAG_UDP.data(); -const char *const TS_PROTO_TAG_IPV4 = IP_PROTO_TAG_IPV4.data(); -const char *const TS_PROTO_TAG_IPV6 = IP_PROTO_TAG_IPV6.data(); +const char *const TS_PROTO_TAG_HTTP_1_0 = TS_ALPN_PROTOCOL_HTTP_1_0; +const char *const TS_PROTO_TAG_HTTP_1_1 = TS_ALPN_PROTOCOL_HTTP_1_1; +const char *const TS_PROTO_TAG_HTTP_2_0 = TS_ALPN_PROTOCOL_HTTP_2_0; +const char *const TS_PROTO_TAG_HTTP_3 = TS_ALPN_PROTOCOL_HTTP_3; +const char *const TS_PROTO_TAG_HTTP_QUIC = TS_ALPN_PROTOCOL_HTTP_QUIC; +const char *const TS_PROTO_TAG_HTTP_3_D27 = TS_ALPN_PROTOCOL_HTTP_3_D27; +const char *const TS_PROTO_TAG_HTTP_QUIC_D27 = TS_ALPN_PROTOCOL_HTTP_QUIC_D27; +const char *const TS_PROTO_TAG_TLS_1_3 = IP_PROTO_TAG_TLS_1_3.data(); +const char *const TS_PROTO_TAG_TLS_1_2 = IP_PROTO_TAG_TLS_1_2.data(); +const char *const TS_PROTO_TAG_TLS_1_1 = IP_PROTO_TAG_TLS_1_1.data(); +const char *const TS_PROTO_TAG_TLS_1_0 = IP_PROTO_TAG_TLS_1_0.data(); +const char *const TS_PROTO_TAG_TCP = IP_PROTO_TAG_TCP.data(); +const char *const TS_PROTO_TAG_UDP = IP_PROTO_TAG_UDP.data(); +const char *const TS_PROTO_TAG_IPV4 = IP_PROTO_TAG_IPV4.data(); +const char *const TS_PROTO_TAG_IPV6 = IP_PROTO_TAG_IPV6.data(); std::unordered_set TSProtoTags; // Precomputed indices for ease of use. -int TS_ALPN_PROTOCOL_INDEX_HTTP_0_9 = SessionProtocolNameRegistry::INVALID; -int TS_ALPN_PROTOCOL_INDEX_HTTP_1_0 = SessionProtocolNameRegistry::INVALID; -int TS_ALPN_PROTOCOL_INDEX_HTTP_1_1 = SessionProtocolNameRegistry::INVALID; -int TS_ALPN_PROTOCOL_INDEX_HTTP_2_0 = SessionProtocolNameRegistry::INVALID; -int TS_ALPN_PROTOCOL_INDEX_HTTP_3 = SessionProtocolNameRegistry::INVALID; -int TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_0_9 = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_1_0 = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_1_1 = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_2_0 = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_3 = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_3_D27 = SessionProtocolNameRegistry::INVALID; +int TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D27 = SessionProtocolNameRegistry::INVALID; // Predefined protocol sets for ease of use. SessionProtocolSet HTTP_PROTOCOL_SET; @@ -703,12 +709,15 @@ void ts_session_protocol_well_known_name_indices_init() { // register all the well known protocols and get the indices set. - TS_ALPN_PROTOCOL_INDEX_HTTP_0_9 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_0_9}); - TS_ALPN_PROTOCOL_INDEX_HTTP_1_0 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_0}); - TS_ALPN_PROTOCOL_INDEX_HTTP_1_1 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_1}); - TS_ALPN_PROTOCOL_INDEX_HTTP_2_0 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_2_0}); - TS_ALPN_PROTOCOL_INDEX_HTTP_3 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_3}); - TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_QUIC}); + TS_ALPN_PROTOCOL_INDEX_HTTP_0_9 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_0_9}); + TS_ALPN_PROTOCOL_INDEX_HTTP_1_0 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_0}); + TS_ALPN_PROTOCOL_INDEX_HTTP_1_1 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_1_1}); + TS_ALPN_PROTOCOL_INDEX_HTTP_2_0 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_2_0}); + TS_ALPN_PROTOCOL_INDEX_HTTP_3 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_3}); + TS_ALPN_PROTOCOL_INDEX_HTTP_3_D27 = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_3_D27}); + TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_QUIC}); + TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D27 = + globalSessionProtocolNameRegistry.toIndexConst(std::string_view{TS_ALPN_PROTOCOL_HTTP_QUIC_D27}); // Now do the predefined protocol sets. HTTP_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_0_9); @@ -722,6 +731,8 @@ ts_session_protocol_well_known_name_indices_init() DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_3); DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC); + DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_3_D27); + DEFAULT_QUIC_SESSION_PROTOCOL_SET.markIn(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC_D27); DEFAULT_NON_TLS_SESSION_PROTOCOL_SET = HTTP_PROTOCOL_SET; @@ -730,6 +741,8 @@ ts_session_protocol_well_known_name_indices_init() TSProtoTags.insert(TS_PROTO_TAG_HTTP_2_0); TSProtoTags.insert(TS_PROTO_TAG_HTTP_3); TSProtoTags.insert(TS_PROTO_TAG_HTTP_QUIC); + TSProtoTags.insert(TS_PROTO_TAG_HTTP_3_D27); + TSProtoTags.insert(TS_PROTO_TAG_HTTP_QUIC_D27); TSProtoTags.insert(TS_PROTO_TAG_TLS_1_3); TSProtoTags.insert(TS_PROTO_TAG_TLS_1_2); TSProtoTags.insert(TS_PROTO_TAG_TLS_1_1); diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc index 4a60e5215d1..30ecbcc8dff 100644 --- a/mgmt/RecordsConfig.cc +++ b/mgmt/RecordsConfig.cc @@ -1442,9 +1442,9 @@ static const RecordElement RecordsConfig[] = // Constatns of Congestion Control {RECT_CONFIG, "proxy.config.quic.congestion_control.max_datagram_size", RECD_INT, "1200", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , - {RECT_CONFIG, "proxy.config.quic.congestion_control.initial_window_scale", RECD_INT, "10", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} + {RECT_CONFIG, "proxy.config.quic.congestion_control.initial_window", RECD_INT, "12000", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , - {RECT_CONFIG, "proxy.config.quic.congestion_control.minimum_window_scale", RECD_INT, "2", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} + {RECT_CONFIG, "proxy.config.quic.congestion_control.minimum_window", RECD_INT, "2400", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , {RECT_CONFIG, "proxy.config.quic.congestion_control.loss_reduction_factor", RECD_FLOAT, "0.5", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[\\.0-9]+$", RECA_NULL} , diff --git a/proxy/http/HttpProxyServerMain.cc b/proxy/http/HttpProxyServerMain.cc index 98941b31169..2dbac63a9ac 100644 --- a/proxy/http/HttpProxyServerMain.cc +++ b/proxy/http/HttpProxyServerMain.cc @@ -238,6 +238,12 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor &acceptor, HttpProxyPort &port, unsigned quic->enableProtocols(port.m_session_protocol_preference); + // HTTP/0.9 over QUIC draft-27 (for interop only, will be removed) + quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_QUIC_D27, new Http3SessionAccept(accept_opt)); + + // HTTP/3 draft-27 + quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3_D27, new Http3SessionAccept(accept_opt)); + // HTTP/0.9 over QUIC (for interop only, will be removed) quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_QUIC, new Http3SessionAccept(accept_opt)); diff --git a/proxy/http3/Http3Session.cc b/proxy/http3/Http3Session.cc index 4d9ceaf066e..aaea1dfd9d3 100644 --- a/proxy/http3/Http3Session.cc +++ b/proxy/http3/Http3Session.cc @@ -29,7 +29,14 @@ // // HQSession // -HQSession ::~HQSession() +HQSession::HQSession(NetVConnection *vc) : ProxySession(vc) +{ + auto app_name = static_cast(vc)->negotiated_application_name(); + memcpy(this->_protocol_string, app_name.data(), std::min(app_name.length(), sizeof(this->_protocol_string))); + this->_protocol_string[app_name.length()] = '\0'; +} + +HQSession::~HQSession() { for (HQTransaction *t = this->_transaction_list.head; t; t = static_cast(t->link.next)) { delete t; @@ -44,6 +51,25 @@ HQSession::add_transaction(HQTransaction *trans) return; } +const char * +HQSession::get_protocol_string() const +{ + return this->_protocol_string; +} + +int +HQSession::populate_protocol(std::string_view *result, int size) const +{ + int retval = 0; + if (size > retval) { + result[retval++] = this->get_protocol_string(); + if (size > retval) { + retval += super::populate_protocol(result + retval, size - retval); + } + } + return retval; +} + HQTransaction * HQSession::get_transaction(QUICStreamId id) { @@ -143,25 +169,6 @@ Http3Session::~Http3Session() delete this->_remote_qpack; } -const char * -Http3Session::get_protocol_string() const -{ - return IP_PROTO_TAG_HTTP_3.data(); -} - -int -Http3Session::populate_protocol(std::string_view *result, int size) const -{ - int retval = 0; - if (size > retval) { - result[retval++] = IP_PROTO_TAG_HTTP_3; - if (size > retval) { - retval += super::populate_protocol(result + retval, size - retval); - } - } - return retval; -} - void Http3Session::increment_current_active_client_connections_stat() { @@ -194,25 +201,6 @@ Http09Session::~Http09Session() this->_vc = nullptr; } -const char * -Http09Session::get_protocol_string() const -{ - return IP_PROTO_TAG_HTTP_QUIC.data(); -} - -int -Http09Session::populate_protocol(std::string_view *result, int size) const -{ - int retval = 0; - if (size > retval) { - result[retval++] = IP_PROTO_TAG_HTTP_QUIC; - if (size > retval) { - retval += super::populate_protocol(result + retval, size - retval); - } - } - return retval; -} - void Http09Session::increment_current_active_client_connections_stat() { diff --git a/proxy/http3/Http3Session.h b/proxy/http3/Http3Session.h index 6bf5fd2cc46..a6aec410d42 100644 --- a/proxy/http3/Http3Session.h +++ b/proxy/http3/Http3Session.h @@ -32,7 +32,7 @@ class HQSession : public ProxySession public: using super = ProxySession; ///< Parent type - HQSession(NetVConnection *vc) : ProxySession(vc){}; + HQSession(NetVConnection *vc); virtual ~HQSession(); // Implement VConnection interface @@ -43,6 +43,8 @@ class HQSession : public ProxySession void reenable(VIO *vio) override; // Implement ProxySession interface + const char *get_protocol_string() const override; + int populate_protocol(std::string_view *result, int size) const override; void new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, IOBufferReader *reader) override; void start() override; void destroy() override; @@ -56,6 +58,8 @@ class HQSession : public ProxySession private: // this should be unordered map? Queue _transaction_list; + + char _protocol_string[16]; }; class Http3Session : public HQSession @@ -67,8 +71,6 @@ class Http3Session : public HQSession ~Http3Session(); // ProxySession interface - const char *get_protocol_string() const override; - int populate_protocol(std::string_view *result, int size) const override; void increment_current_active_client_connections_stat() override; void decrement_current_active_client_connections_stat() override; @@ -92,8 +94,6 @@ class Http09Session : public HQSession ~Http09Session(); // ProxySession interface - const char *get_protocol_string() const override; - int populate_protocol(std::string_view *result, int size) const override; void increment_current_active_client_connections_stat() override; void decrement_current_active_client_connections_stat() override; diff --git a/proxy/http3/Http3SessionAccept.cc b/proxy/http3/Http3SessionAccept.cc index f5d1ff66419..93dfbd88bb4 100644 --- a/proxy/http3/Http3SessionAccept.cc +++ b/proxy/http3/Http3SessionAccept.cc @@ -62,16 +62,16 @@ Http3SessionAccept::accept(NetVConnection *netvc, MIOBuffer *iobuf, IOBufferRead std::string_view alpn = qvc->negotiated_application_name(); - if (alpn.empty() || IP_PROTO_TAG_HTTP_QUIC.compare(alpn) == 0) { + if (alpn.empty() || IP_PROTO_TAG_HTTP_QUIC.compare(alpn) == 0 || IP_PROTO_TAG_HTTP_QUIC_D27.compare(alpn) == 0) { if (alpn.empty()) { Debug("http3", "[%s] start HTTP/0.9 app (ALPN=null)", qvc->cids().data()); } else { - Debug("http3", "[%s] start HTTP/0.9 app (ALPN=%s)", qvc->cids().data(), IP_PROTO_TAG_HTTP_QUIC.data()); + Debug("http3", "[%s] start HTTP/0.9 app (ALPN=%.*s)", qvc->cids().data(), static_cast(alpn.length()), alpn.data()); } new Http09App(qvc, std::move(session_acl), this->options); - } else if (IP_PROTO_TAG_HTTP_3.compare(alpn) == 0) { - Debug("http3", "[%s] start HTTP/3 app (ALPN=%s)", qvc->cids().data(), IP_PROTO_TAG_HTTP_3.data()); + } else if (IP_PROTO_TAG_HTTP_3.compare(alpn) == 0 || IP_PROTO_TAG_HTTP_3_D27.compare(alpn) == 0) { + Debug("http3", "[%s] start HTTP/3 app (ALPN=%.*s)", qvc->cids().data(), static_cast(alpn.length()), alpn.data()); Http3App *app = new Http3App(qvc, std::move(session_acl), this->options); app->start(); diff --git a/src/traffic_quic/quic_client.cc b/src/traffic_quic/quic_client.cc index 8a65c288c0a..04fb44d654c 100644 --- a/src/traffic_quic/quic_client.cc +++ b/src/traffic_quic/quic_client.cc @@ -34,8 +34,8 @@ // https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_alpn_protos.html // Should be integrate with IP_PROTO_TAG_HTTP_QUIC in ts/ink_inet.h ? using namespace std::literals; -static constexpr std::string_view HQ_ALPN_PROTO_LIST("\5hq-27"sv); -static constexpr std::string_view H3_ALPN_PROTO_LIST("\5h3-27"sv); +static constexpr std::string_view HQ_ALPN_PROTO_LIST("\5hq-29\5hq-27"sv); +static constexpr std::string_view H3_ALPN_PROTO_LIST("\5h3-29\5h3-27"sv); QUICClient::QUICClient(const QUICClientConfig *config) : Continuation(new_ProxyMutex()), _config(config) { diff --git a/src/tscore/ink_inet.cc b/src/tscore/ink_inet.cc index 9d1dff707eb..6f781cc85c0 100644 --- a/src/tscore/ink_inet.cc +++ b/src/tscore/ink_inet.cc @@ -49,9 +49,11 @@ const std::string_view IP_PROTO_TAG_TLS_1_3("tls/1.3"sv); const std::string_view IP_PROTO_TAG_HTTP_0_9("http/0.9"sv); const std::string_view IP_PROTO_TAG_HTTP_1_0("http/1.0"sv); const std::string_view IP_PROTO_TAG_HTTP_1_1("http/1.1"sv); -const std::string_view IP_PROTO_TAG_HTTP_2_0("h2"sv); // HTTP/2 over TLS -const std::string_view IP_PROTO_TAG_HTTP_QUIC("hq-27"sv); // HTTP/0.9 over QUIC -const std::string_view IP_PROTO_TAG_HTTP_3("h3-27"sv); // HTTP/3 over QUIC +const std::string_view IP_PROTO_TAG_HTTP_2_0("h2"sv); // HTTP/2 over TLS +const std::string_view IP_PROTO_TAG_HTTP_QUIC("hq-29"sv); // HTTP/0.9 over QUIC +const std::string_view IP_PROTO_TAG_HTTP_3("h3-29"sv); // HTTP/3 over QUIC +const std::string_view IP_PROTO_TAG_HTTP_QUIC_D27("hq-27"sv); // HTTP/0.9 over QUIC (draft-27) +const std::string_view IP_PROTO_TAG_HTTP_3_D27("h3-27"sv); // HTTP/3 over QUIC (draft-27) const std::string_view UNIX_PROTO_TAG{"unix"sv};