diff --git a/configure.ac b/configure.ac index 4d02f4637c8..ea97c898dbd 100644 --- a/configure.ac +++ b/configure.ac @@ -1261,18 +1261,31 @@ enable_quic=no AC_MSG_CHECKING([whether APIs for QUIC are available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ - #ifdef OPENSSL_IS_BORINGSSL SSL_QUIC_METHOD var; + ]]) + ], + [ + AC_MSG_RESULT([yes]) + enable_quic=yes + _quic_saved_LIBS=$LIBS + TS_ADDTO(LIBS, [$OPENSSL_LIBS]) + AC_CHECK_FUNCS(SSL_set_quic_early_data_enabled) + LIBS=$_quic_saved_LIBS + ], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ + #ifdef SSL_MODE_QUIC_HACK #else - #ifndef SSL_MODE_QUIC_HACK # error no hack for quic #endif - #endif ]]) ], - [AC_MSG_RESULT([yes]); enable_quic=yes], + [AC_MSG_RESULT([yes]); enable_quic=yes; enable_quic_old_api=yes], [AC_MSG_RESULT([no])]) + ]) + AM_CONDITIONAL([ENABLE_QUIC], [test "x$enable_quic" = "xyes"]) +AM_CONDITIONAL([ENABLE_QUIC_OLD_API], [test "x$enable_quic_old_api" = "xyes"]) TS_ARG_ENABLE_VAR([use], [quic]) AC_SUBST(use_quic) diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst index 9c24300c04f..6f17595679d 100644 --- a/doc/admin-guide/files/records.config.en.rst +++ b/doc/admin-guide/files/records.config.en.rst @@ -3897,6 +3897,12 @@ QUIC Configuration All configurations for QUIC are still experimental and may be changed or removed in the future without prior notice. +.. ts:cv:: CONFIG proxy.config.quic.qlog_dir STRING NULL + :reloadable: + + The qlog is enabled when this configuration is not NULL. And will dump + the qlog to this dir. + .. ts:cv:: CONFIG proxy.config.quic.instance_id INT 0 :reloadable: diff --git a/iocore/net/P_Net.h b/iocore/net/P_Net.h index 46a05c3a784..b3027ce155a 100644 --- a/iocore/net/P_Net.h +++ b/iocore/net/P_Net.h @@ -111,13 +111,6 @@ extern RecRawStatBlock *net_rsb; #include "P_SSLNetAccept.h" #include "P_SSLCertLookup.h" -#if TS_USE_QUIC == 1 -#include "P_QUICNetVConnection.h" -#include "P_QUICNetProcessor.h" -#include "P_QUICPacketHandler.h" -#include "P_QUICNet.h" -#endif - static constexpr ts::ModuleVersion NET_SYSTEM_MODULE_INTERNAL_VERSION(NET_SYSTEM_MODULE_PUBLIC_VERSION, ts::ModuleVersion::PRIVATE); // For very verbose iocore debugging. diff --git a/iocore/net/P_QUICNet.h b/iocore/net/P_QUICNet.h index 802bf6ed6e6..a8a59a20199 100644 --- a/iocore/net/P_QUICNet.h +++ b/iocore/net/P_QUICNet.h @@ -29,6 +29,9 @@ #include "tscore/ink_platform.h" #include "P_Net.h" +#include "quic/QUICTypes.h" +#include "P_QUICNetProcessor.h" +#include "P_QUICNetVConnection.h" class NetHandler; typedef int (NetHandler::*NetContHandler)(int, void *); diff --git a/iocore/net/P_QUICNetProcessor.h b/iocore/net/P_QUICNetProcessor.h index bb3e8576b74..68e722814d5 100644 --- a/iocore/net/P_QUICNetProcessor.h +++ b/iocore/net/P_QUICNetProcessor.h @@ -42,6 +42,7 @@ #include "quic/QUICConnectionTable.h" class UnixNetVConnection; +class QUICResetTokenTable; struct NetAccept; ////////////////////////////////////////////////////////////////// @@ -73,6 +74,7 @@ class QUICNetProcessor : public UnixNetProcessor QUICNetProcessor &operator=(const QUICNetProcessor &); QUICConnectionTable *_ctable = nullptr; + QUICResetTokenTable *_rtable = nullptr; }; extern QUICNetProcessor quic_NetProcessor; diff --git a/iocore/net/P_QUICNetVConnection.h b/iocore/net/P_QUICNetVConnection.h index d39824e8074..a7aee81831d 100644 --- a/iocore/net/P_QUICNetVConnection.h +++ b/iocore/net/P_QUICNetVConnection.h @@ -37,18 +37,20 @@ #include "P_UnixNetVConnection.h" #include "P_UnixNet.h" #include "P_UDPNet.h" +#include "P_ALPNSupport.h" +#include "TLSSessionResumptionSupport.h" #include "tscore/ink_apidefs.h" #include "tscore/List.h" #include "quic/QUICConfig.h" #include "quic/QUICConnection.h" #include "quic/QUICConnectionTable.h" +#include "quic/QUICResetTokenTable.h" #include "quic/QUICVersionNegotiator.h" #include "quic/QUICPacket.h" #include "quic/QUICPacketFactory.h" #include "quic/QUICFrame.h" #include "quic/QUICFrameDispatcher.h" -#include "quic/QUICHandshake.h" #include "quic/QUICApplication.h" #include "quic/QUICStream.h" #include "quic/QUICHandshakeProtocol.h" @@ -62,10 +64,12 @@ #include "quic/QUICPathManager.h" #include "quic/QUICApplicationMap.h" #include "quic/QUICPacketReceiveQueue.h" +#include "quic/QUICPacketHeaderProtector.h" #include "quic/QUICAddrVerifyState.h" #include "quic/QUICPacketProtectionKeyInfo.h" #include "quic/QUICContext.h" #include "quic/QUICTokenCreator.h" +#include "quic/qlog/QLogListener.h" // Size of connection ids for debug log : e.g. aaaaaaaa-bbbbbbbb\0 static constexpr size_t MAX_CIDS_SIZE = 8 + 1 + 8 + 1; @@ -80,6 +84,7 @@ static constexpr size_t MAX_CIDS_SIZE = 8 + 1 + 8 + 1; class QUICPacketHandler; class QUICLossDetector; +class QUICHandshake; class SSLNextProtocolSet; @@ -131,16 +136,21 @@ class SSLNextProtocolSet; * WRITE: * Do nothing **/ -class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, public RefCountObj, public ALPNSupport +class QUICNetVConnection : public UnixNetVConnection, + public QUICConnection, + public RefCountObj, + public ALPNSupport, + public TLSSessionResumptionSupport { using super = UnixNetVConnection; ///< Parent type. public: QUICNetVConnection(); ~QUICNetVConnection(); - void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *); + void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *, + QUICResetTokenTable *rtable); void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, UDPConnection *, - QUICPacketHandler *, QUICConnectionTable *ctable); + QUICPacketHandler *, QUICResetTokenTable *rtable, QUICConnectionTable *ctable); // accept new conn_id int acceptEvent(int event, Event *e); @@ -180,7 +190,8 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub // QUICConnection QUICStreamManager *stream_manager() override; - void close(QUICConnectionErrorUPtr error) override; + void close_quic_connection(QUICConnectionErrorUPtr error) override; + void reset_quic_connection() override; void handle_received_packet(UDPPacket *packet) override; void ping() override; @@ -207,6 +218,9 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub LINK(QUICNetVConnection, closed_link); SLINK(QUICNetVConnection, closed_alink); +protected: + const IpEndpoint &_getLocalEndpoint() override; + private: std::random_device _rnd; @@ -247,6 +261,7 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub QUICCongestionController *_congestion_controller = nullptr; QUICRemoteFlowController *_remote_flow_controller = nullptr; QUICLocalFlowController *_local_flow_controller = nullptr; + QUICResetTokenTable *_rtable = nullptr; QUICConnectionTable *_ctable = nullptr; QUICAltConnectionManager *_alt_con_manager = nullptr; QUICPathValidator *_path_validator = nullptr; @@ -290,22 +305,23 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub Ptr _store_frame(Ptr parent_block, size_t &size_added, uint64_t &max_frame_size, QUICFrame &frame, std::vector &frames); - QUICPacketUPtr _packetize_frames(QUICEncryptionLevel level, uint64_t max_packet_size, std::vector &frames); + QUICPacketUPtr _packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel level, uint64_t max_packet_size, + std::vector &frames); void _packetize_closing_frame(); - QUICPacketUPtr _build_packet(QUICEncryptionLevel level, Ptr parent_block, bool retransmittable, bool probing, - bool crypto); + QUICPacketUPtr _build_packet(uint8_t *packet_buf, QUICEncryptionLevel level, Ptr parent_block, + bool retransmittable, bool probing, bool crypto); - QUICConnectionErrorUPtr _recv_and_ack(const QUICPacket &packet, bool *has_non_probing_frame = nullptr); + QUICConnectionErrorUPtr _recv_and_ack(const QUICPacketR &packet, bool *has_non_probing_frame = nullptr); QUICConnectionErrorUPtr _state_handshake_process_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_version_negotiation_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_initial_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_retry_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_handshake_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_zero_rtt_protected_packet(const QUICPacket &packet); + QUICConnectionErrorUPtr _state_handshake_process_version_negotiation_packet(const QUICVersionNegotiationPacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_initial_packet(const QUICInitialPacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_retry_packet(const QUICRetryPacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_handshake_packet(const QUICHandshakePacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_zero_rtt_protected_packet(const QUICZeroRttPacketR &packet); QUICConnectionErrorUPtr _state_connection_established_receive_packet(); - QUICConnectionErrorUPtr _state_connection_established_process_protected_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_connection_established_migrate_connection(const QUICPacket &packet); + QUICConnectionErrorUPtr _state_connection_established_process_protected_packet(const QUICShortHeaderPacketR &packet); + QUICConnectionErrorUPtr _state_connection_established_migrate_connection(const QUICPacketR &packet); QUICConnectionErrorUPtr _state_connection_established_initiate_connection_migration(); QUICConnectionErrorUPtr _state_closing_receive_packet(); QUICConnectionErrorUPtr _state_draining_receive_packet(); @@ -318,7 +334,7 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub void _init_flow_control_params(const std::shared_ptr &local_tp, const std::shared_ptr &remote_tp); void _handle_error(QUICConnectionErrorUPtr error); - QUICPacketUPtr _dequeue_recv_packet(QUICPacketCreationResult &result); + QUICPacketUPtr _dequeue_recv_packet(uint8_t *packet_buf, QUICPacketCreationResult &result); void _validate_new_path(const QUICPath &path); int _complete_handshake_if_possible(); @@ -344,6 +360,7 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub QUICHandshakeProtocol *_setup_handshake_protocol(shared_SSL_CTX ctx); QUICPacketUPtr _the_final_packet = QUICPacketFactory::create_null_packet(); + uint8_t _final_packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICStatelessResetToken _reset_token; ats_unique_buf _av_token = {nullptr}; @@ -353,9 +370,11 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub uint32_t _seq_num = 0; // TODO: Source addresses verification through an address validation token - QUICAddrVerifyState _verfied_state; + QUICAddrVerifyState _verified_state; + + std::unique_ptr _context; - std::unique_ptr _context; + std::shared_ptr _qlog; }; typedef int (QUICNetVConnection::*QUICNetVConnHandler)(int, void *); diff --git a/iocore/net/P_QUICPacketHandler.h b/iocore/net/P_QUICPacketHandler.h index 27a5235d911..4df92ece805 100644 --- a/iocore/net/P_QUICPacketHandler.h +++ b/iocore/net/P_QUICPacketHandler.h @@ -28,6 +28,7 @@ #include "P_NetAccept.h" #include "quic/QUICTypes.h" #include "quic/QUICConnectionTable.h" +#include "quic/QUICResetTokenTable.h" class QUICClosedConCollector; class QUICNetVConnection; @@ -37,7 +38,7 @@ class QUICPacketHeaderProtector; class QUICPacketHandler { public: - QUICPacketHandler(); + QUICPacketHandler(QUICResetTokenTable &rtable); ~QUICPacketHandler(); void send_packet(const QUICPacket &packet, QUICNetVConnection *vc, const QUICPacketHeaderProtector &pn_protector); @@ -49,6 +50,7 @@ class QUICPacketHandler void _send_packet(const QUICPacket &packet, UDPConnection *udp_con, IpEndpoint &addr, uint32_t pmtu, const QUICPacketHeaderProtector *ph_protector, int dcil); void _send_packet(UDPConnection *udp_con, IpEndpoint &addr, Ptr udp_payload); + QUICConnection *_check_stateless_reset(const uint8_t *buf, size_t buf_len); // FIXME Remove this // QUICPacketHandler could be a continuation, but NetAccept is a contination too. @@ -58,6 +60,8 @@ class QUICPacketHandler QUICClosedConCollector *_closed_con_collector = nullptr; virtual void _recv_packet(int event, UDPPacket *udpPacket) = 0; + + QUICResetTokenTable &_rtable; }; /* @@ -67,7 +71,7 @@ class QUICPacketHandler class QUICPacketHandlerIn : public NetAccept, public QUICPacketHandler { public: - QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable); + QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable, QUICResetTokenTable &rtable); ~QUICPacketHandlerIn(); // NetAccept @@ -84,6 +88,10 @@ class QUICPacketHandlerIn : public NetAccept, public QUICPacketHandler 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); + 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, + IpEndpoint from); QUICConnectionTable &_ctable; }; @@ -95,7 +103,7 @@ class QUICPacketHandlerIn : public NetAccept, public QUICPacketHandler class QUICPacketHandlerOut : public Continuation, public QUICPacketHandler { public: - QUICPacketHandlerOut(); + QUICPacketHandlerOut(QUICResetTokenTable &rtable); ~QUICPacketHandlerOut(){}; void init(QUICNetVConnection *vc); diff --git a/iocore/net/QUICNet.cc b/iocore/net/QUICNet.cc index d3df166be11..34e21ae2272 100644 --- a/iocore/net/QUICNet.cc +++ b/iocore/net/QUICNet.cc @@ -22,6 +22,7 @@ */ #include "P_Net.h" +#include "P_QUICNet.h" #include "quic/QUICEvents.h" ClassAllocator quicPollEventAllocator("quicPollEvent"); @@ -68,7 +69,7 @@ QUICPollCont::_process_long_header_packet(QUICPollEvent *e, NetHandler *nh) uint8_t *buf = (uint8_t *)p->getIOBlockChain()->buf(); QUICPacketType ptype; - QUICPacketLongHeader::type(ptype, buf, 1); + QUICLongHeaderPacketR::type(ptype, buf, 1); if (ptype == QUICPacketType::INITIAL && !vc->read.triggered) { SCOPED_MUTEX_LOCK(lock, vc->mutex, this_ethread()); vc->read.triggered = 1; diff --git a/iocore/net/QUICNetProcessor.cc b/iocore/net/QUICNetProcessor.cc index a0c706a6d34..4dfebae1487 100644 --- a/iocore/net/QUICNetProcessor.cc +++ b/iocore/net/QUICNetProcessor.cc @@ -25,9 +25,13 @@ #include "P_Net.h" #include "records/I_RecHttp.h" +#include "P_QUICNetProcessor.h" +#include "P_QUICNet.h" +#include "P_QUICPacketHandler.h" #include "QUICGlobals.h" #include "QUICConfig.h" #include "QUICMultiCertConfigLoader.h" +#include "QUICResetTokenTable.h" // // Global Data @@ -76,8 +80,9 @@ QUICNetProcessor::createNetAccept(const NetProcessor::AcceptOptions &opt) if (this->_ctable == nullptr) { QUICConfig::scoped_config params; this->_ctable = new QUICConnectionTable(params->connection_table_size()); + this->_rtable = new QUICResetTokenTable(); } - return (NetAccept *)new QUICPacketHandlerIn(opt, *this->_ctable); + return (NetAccept *)new QUICPacketHandlerIn(opt, *this->_ctable, *this->_rtable); } NetVConnection * @@ -125,7 +130,8 @@ QUICNetProcessor::connect_re(Continuation *cont, sockaddr const *remote_addr, Ne UnixUDPConnection *con = new UnixUDPConnection(fd); Debug("quic_ps", "con=%p fd=%d", con, fd); - QUICPacketHandlerOut *packet_handler = new QUICPacketHandlerOut(); + this->_rtable = new QUICResetTokenTable(); + QUICPacketHandlerOut *packet_handler = new QUICPacketHandlerOut(*this->_rtable); if (opt->local_ip.isValid()) { con->setBinding(opt->local_ip, opt->local_port); } @@ -144,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); + vc->init(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 004867d0bcf..833875337f4 100644 --- a/iocore/net/QUICNetVConnection.cc +++ b/iocore/net/QUICNetVConnection.cc @@ -27,6 +27,8 @@ #include "records/I_RecHttp.h" #include "tscore/Diags.h" +#include "P_QUICNetVConnection.h" +#include "P_QUICPacketHandler.h" #include "P_Net.h" #include "InkAPIInternal.h" // Added to include the quic_hook definitions #include "Log.h" @@ -39,12 +41,16 @@ #include "QUICGlobals.h" #include "QUICDebugNames.h" #include "QUICEvents.h" +#include "QUICHandshake.h" #include "QUICConfig.h" #include "QUICIntUtil.h" using namespace std::literals; static constexpr std::string_view QUIC_DEBUG_TAG = "quic_net"sv; +static constexpr uint16_t QUANTUM_TEST_ID = 3127; +static constexpr uint8_t QUANTUM_TEST_VALUE[1200] = {'Q'}; + #define QUICConDebug(fmt, ...) Debug(QUIC_DEBUG_TAG.data(), "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) #define QUICConVDebug(fmt, ...) Debug("v_quic_net", "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) @@ -188,9 +194,33 @@ class QUICTPConfigQCP : public QUICTPConfig } } + bool + disable_active_migration() const override + { + if (this->_ctx == NET_VCONNECTION_IN) { + return this->_params->disable_active_migration(); + } else { + return false; + } + } + + std::unordered_map> + additional_tp() const override + { + return this->_additional_tp; + } + + void + add_tp(uint16_t id, const uint8_t *value, uint16_t length) + { + this->_additional_tp.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(value, length)); + } + private: const QUICConfigParams *_params; NetVConnectionContext_t _ctx; + + std::unordered_map> _additional_tp; }; QUICNetVConnection::QUICNetVConnection() : _packet_factory(this->_pp_key_info), _ph_protector(this->_pp_key_info) {} @@ -207,31 +237,28 @@ QUICNetVConnection::~QUICNetVConnection() // Initialize QUICNetVC for out going connection (NET_VCONNECTION_OUT) void QUICNetVConnection::init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *udp_con, - QUICPacketHandler *packet_handler) + QUICPacketHandler *packet_handler, QUICResetTokenTable *rtable) { SET_HANDLER((NetVConnHandler)&QUICNetVConnection::startEvent); 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->_update_cids(); if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char dcid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char scid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_peer_quic_connection_id.hex(dcid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - this->_quic_connection_id.hex(scid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("dcid=%s scid=%s", dcid_hex_str, scid_hex_str); + QUICConDebug("dcid=%s scid=%s", this->_peer_quic_connection_id.hex().c_str(), this->_quic_connection_id.hex().c_str()); } } // 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, QUICConnectionTable *ctable) + UDPConnection *udp_con, QUICPacketHandler *packet_handler, QUICResetTokenTable *rtable, + QUICConnectionTable *ctable) { SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent); this->_udp_con = udp_con; @@ -246,16 +273,12 @@ QUICNetVConnection::init(QUICConnectionId peer_cid, QUICConnectionId original_ci this->_ctable->insert(this->_quic_connection_id, this); this->_ctable->insert(this->_original_quic_connection_id, this); } + this->_rtable = rtable; this->_update_cids(); if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char dcid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char scid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_peer_quic_connection_id.hex(dcid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - this->_quic_connection_id.hex(scid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("dcid=%s scid=%s", dcid_hex_str, scid_hex_str); + QUICConDebug("dcid=%s scid=%s", this->_peer_quic_connection_id.hex().c_str(), this->_quic_connection_id.hex().c_str()); } } @@ -356,7 +379,17 @@ void QUICNetVConnection::start() { ink_release_assert(this->thread != nullptr); - this->_context = std::make_unique(&this->_rtt_measure, this, &this->_pp_key_info); + 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 @@ -374,6 +407,9 @@ QUICNetVConnection::start() } else { trusted_path = {this->local_addr, this->remote_addr}; QUICTPConfigQCP tp_config(this->_quic_config, NET_VCONNECTION_OUT); + if (this->_quic_config->quantum_readiness_test_enabled_out()) { + tp_config.add_tp(QUANTUM_TEST_ID, QUANTUM_TEST_VALUE, sizeof(QUANTUM_TEST_VALUE)); + } this->_pp_key_info.set_context(QUICPacketProtectionKeyInfo::Context::CLIENT); this->_ack_frame_manager.set_ack_delay_exponent(this->_quic_config->ack_delay_exponent_out()); this->_hs_protocol = this->_setup_handshake_protocol(this->_quic_config->client_ssl_ctx()); @@ -383,6 +419,7 @@ QUICNetVConnection::start() this->_ack_frame_manager.set_max_ack_delay(this->_quic_config->max_ack_delay_out()); this->_schedule_ack_manager_periodic(this->_quic_config->max_ack_delay_out()); } + this->_path_manager->set_trusted_path(trusted_path); this->_application_map = new QUICApplicationMap(); @@ -399,19 +436,8 @@ QUICNetVConnection::start() this->_remote_flow_controller = new QUICRemoteConnectionFlowController(UINT64_MAX); this->_local_flow_controller = new QUICLocalConnectionFlowController(&this->_rtt_measure, UINT64_MAX); - 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->_stream_manager = new QUICStreamManager(this, &this->_rtt_measure, this->_application_map); - this->_path_manager = new QUICPathManager(*this, *this->_path_validator); - this->_path_manager->set_trusted_path(trusted_path); - this->_token_creator = new QUICTokenCreator(this->_context.get()); + this->_stream_manager = new QUICStreamManager(this->_context.get(), this->_application_map); + this->_token_creator = new QUICTokenCreator(this->_context.get()); static constexpr int QUIC_STREAM_MANAGER_WEIGHT = QUICFrameGeneratorWeight::AFTER_DATA - 1; static constexpr int QUIC_PINGER_WEIGHT = QUICFrameGeneratorWeight::LATE + 1; @@ -436,6 +462,14 @@ QUICNetVConnection::start() this->_frame_dispatcher->add_handler(this->_stream_manager); this->_frame_dispatcher->add_handler(this->_path_validator); this->_frame_dispatcher->add_handler(this->_handshake_handler); + + // regist qlog + if (this->_context->config()->qlog_dir() != nullptr) { + this->_qlog = std::make_unique(*this->_context, this->_original_quic_connection_id.hex()); + this->_qlog->last_trace().set_vantage_point( + {"ats", QLog::Trace::VantagePointType::server, QLog::Trace::VantagePointType::server}); + this->_context->regist_callback(this->_qlog); + } } void @@ -462,6 +496,7 @@ QUICNetVConnection::free(EThread *t) super::clear(); */ + this->_context->trigger(QUICContext::CallbackEvent::CONNECTION_CLOSE); ALPNSupport::clear(); this->_packet_handler->close_connection(this); } @@ -641,11 +676,11 @@ QUICNetVConnection::handle_received_packet(UDPPacket *packet) void QUICNetVConnection::ping() { - this->_pinger->request(); + this->_pinger->request(QUICEncryptionLevel::ONE_RTT); } void -QUICNetVConnection::close(QUICConnectionErrorUPtr error) +QUICNetVConnection::close_quic_connection(QUICConnectionErrorUPtr error) { if (this->handler == reinterpret_cast(&QUICNetVConnection::state_connection_closed) || this->handler == reinterpret_cast(&QUICNetVConnection::state_connection_closing)) { @@ -655,6 +690,24 @@ QUICNetVConnection::close(QUICConnectionErrorUPtr error) } } +void +QUICNetVConnection::reset_quic_connection() +{ + this->_switch_to_close_state(); + + QUICStatelessResetToken token(this->connection_id(), this->_quic_config->instance_id()); + auto packet = QUICPacketFactory::create_stateless_reset_packet(token, 128); + if (packet) { + Ptr udp_payload(new_IOBufferBlock()); + udp_payload->alloc(iobuffer_size_to_index(128, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(udp_payload->end()); + size_t len = 0; + packet->store(buf, &len); + udp_payload->fill(len); + this->_packet_handler->send_packet(this, udp_payload); + } +} + std::vector QUICNetVConnection::interests() { @@ -745,7 +798,8 @@ QUICNetVConnection::state_handshake(int event, Event *data) QUICPacketCreationResult result; net_activity(this, this_ethread()); do { - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::NOT_READY) { error = nullptr; } else if (result == QUICPacketCreationResult::FAILED) { @@ -767,6 +821,9 @@ QUICNetVConnection::state_handshake(int event, Event *data) } while (error == nullptr && (result == QUICPacketCreationResult::SUCCESS || result == QUICPacketCreationResult::IGNORED)); break; } + case QUIC_EVENT_STATELESS_RESET: + this->_switch_to_draining_state(std::make_unique(QUICTransErrorCode::NO_ERROR, "Stateless Reset")); + break; case QUIC_EVENT_ACK_PERIODIC: this->_handle_periodic_ack_event(); break; @@ -810,6 +867,9 @@ QUICNetVConnection::state_connection_established(int event, Event *data) // Reschedule WRITE_READY this->_schedule_packet_write_ready(true); break; + case QUIC_EVENT_STATELESS_RESET: + this->_switch_to_draining_state(std::make_unique(QUICTransErrorCode::NO_ERROR, "Stateless Reset")); + break; case VC_EVENT_INACTIVITY_TIMEOUT: // Start Immediate Close because of Idle Timeout this->_handle_idle_timeout(); @@ -844,6 +904,8 @@ QUICNetVConnection::state_connection_closing(int event, Event *data) this->_close_closing_timeout(data); this->_switch_to_close_state(); break; + case QUIC_EVENT_STATELESS_RESET: + break; case QUIC_EVENT_ACK_PERIODIC: default: QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); @@ -872,6 +934,8 @@ QUICNetVConnection::state_connection_draining(int event, Event *data) this->_close_closing_timeout(data); this->_switch_to_close_state(); break; + case QUIC_EVENT_STATELESS_RESET: + break; case QUIC_EVENT_ACK_PERIODIC: default: QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); @@ -1024,28 +1088,28 @@ QUICNetVConnection::_state_handshake_process_packet(const QUICPacket &packet) QUICConnectionErrorUPtr error = nullptr; switch (packet.type()) { case QUICPacketType::VERSION_NEGOTIATION: - error = this->_state_handshake_process_version_negotiation_packet(packet); + error = this->_state_handshake_process_version_negotiation_packet(static_cast(packet)); break; case QUICPacketType::INITIAL: - error = this->_state_handshake_process_initial_packet(packet); + error = this->_state_handshake_process_initial_packet(static_cast(packet)); break; case QUICPacketType::RETRY: - error = this->_state_handshake_process_retry_packet(packet); + error = this->_state_handshake_process_retry_packet(static_cast(packet)); break; case QUICPacketType::HANDSHAKE: - error = this->_state_handshake_process_handshake_packet(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->_minimum_encryption_level = QUICEncryptionLevel::HANDSHAKE; } break; case QUICPacketType::ZERO_RTT_PROTECTED: - error = this->_state_handshake_process_zero_rtt_protected_packet(packet); + error = this->_state_handshake_process_zero_rtt_protected_packet(static_cast(packet)); break; case QUICPacketType::PROTECTED: - default: QUICConDebug("Ignore %s(%" PRIu8 ") packet", QUICDebugNames::packet_type(packet.type()), static_cast(packet.type())); - + break; + default: error = std::make_unique(QUICTransErrorCode::INTERNAL_ERROR); break; } @@ -1053,7 +1117,7 @@ QUICNetVConnection::_state_handshake_process_packet(const QUICPacket &packet) } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_version_negotiation_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_version_negotiation_packet(const QUICVersionNegotiationPacketR &packet) { QUICConnectionErrorUPtr error = nullptr; @@ -1082,7 +1146,7 @@ QUICNetVConnection::_state_handshake_process_version_negotiation_packet(const QU } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_initial_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_initial_packet(const QUICInitialPacketR &packet) { // QUIC packet could be smaller than MINIMUM_INITIAL_PACKET_SIZE when coalescing packets // if (packet->size() < MINIMUM_INITIAL_PACKET_SIZE) { @@ -1101,28 +1165,31 @@ QUICNetVConnection::_state_handshake_process_initial_packet(const QUICPacket &pa if (!this->_alt_con_manager) { this->_alt_con_manager = - new QUICAltConnectionManager(this, *this->_ctable, this->_peer_quic_connection_id, this->_quic_config->instance_id(), - this->_quic_config->active_cid_limit_in(), this->_quic_config->preferred_address_ipv4(), - this->_quic_config->preferred_address_ipv6()); + new QUICAltConnectionManager(this, *this->_ctable, *this->_rtable, this->_peer_quic_connection_id, + this->_quic_config->instance_id(), this->_quic_config->active_cid_limit_in(), + this->_quic_config->preferred_address_ipv4(), this->_quic_config->preferred_address_ipv6()); this->_frame_generators.add_generator(*this->_alt_con_manager, QUICFrameGeneratorWeight::EARLY); this->_frame_dispatcher->add_handler(this->_alt_con_manager); } QUICTPConfigQCP tp_config(this->_quic_config, NET_VCONNECTION_IN); + if (this->_quic_config->quantum_readiness_test_enabled_in()) { + tp_config.add_tp(QUANTUM_TEST_ID, QUANTUM_TEST_VALUE, sizeof(QUANTUM_TEST_VALUE)); + } error = this->_handshake_handler->start(tp_config, packet, &this->_packet_factory, this->_alt_con_manager->preferred_address()); // If version negotiation was failed and VERSION NEGOTIATION packet was sent, nothing to do. if (this->_handshake_handler->is_version_negotiated()) { error = this->_recv_and_ack(packet); - if (error == nullptr && !this->_handshake_handler->has_remote_tp()) { + if (error == nullptr && this->_handshake_handler->is_completed() && !this->_handshake_handler->has_remote_tp()) { error = std::make_unique(QUICTransErrorCode::TRANSPORT_PARAMETER_ERROR); } } } else { if (!this->_alt_con_manager) { this->_alt_con_manager = - new QUICAltConnectionManager(this, *this->_ctable, this->_peer_quic_connection_id, this->_quic_config->instance_id(), - this->_quic_config->active_cid_limit_out()); + new QUICAltConnectionManager(this, *this->_ctable, *this->_rtable, this->_peer_quic_connection_id, + this->_quic_config->instance_id(), this->_quic_config->active_cid_limit_out()); this->_frame_generators.add_generator(*this->_alt_con_manager, QUICFrameGeneratorWeight::BEFORE_DATA); this->_frame_dispatcher->add_handler(this->_alt_con_manager); } @@ -1138,7 +1205,7 @@ QUICNetVConnection::_state_handshake_process_initial_packet(const QUICPacket &pa This doesn't call this->_recv_and_ack(), because RETRY packet doesn't have any frames. */ QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_retry_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_retry_packet(const QUICRetryPacketR &packet) { ink_assert(this->netvc_context == NET_VCONNECTION_OUT); @@ -1147,10 +1214,19 @@ QUICNetVConnection::_state_handshake_process_retry_packet(const QUICPacket &pack return nullptr; } + // Check Integrity Tag + if (!packet.has_valid_tag(this->_original_quic_connection_id)) { + // Discard the packet + QUICConDebug("Ignore RETRY packet - integrity tag is not valid"); + return nullptr; + } else { + QUICConDebug("Integrity tag is valid"); + } + // TODO: move packet->payload to _av_token - this->_av_token_len = packet.payload_length(); + this->_av_token_len = packet.token().length(); this->_av_token = ats_unique_malloc(this->_av_token_len); - memcpy(this->_av_token.get(), packet.payload(), this->_av_token_len); + memcpy(this->_av_token.get(), packet.token().buf(), this->_av_token_len); this->_padder->set_av_token_len(this->_av_token_len); @@ -1174,18 +1250,18 @@ QUICNetVConnection::_state_handshake_process_retry_packet(const QUICPacket &pack } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_handshake_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_handshake_packet(const QUICHandshakePacketR &packet) { // Source address is verified by receiving any message from the client encrypted using the // Handshake keys. - if (this->netvc_context == NET_VCONNECTION_IN && !this->_verfied_state.is_verified()) { - this->_verfied_state.set_addr_verifed(); + if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { + this->_verified_state.set_addr_verifed(); } return this->_recv_and_ack(packet); } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(const QUICZeroRttPacketR &packet) { this->_stream_manager->init_flow_control_params(this->_handshake_handler->local_transport_parameters(), this->_handshake_handler->remote_transport_parameters()); @@ -1194,7 +1270,7 @@ QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(const QUI } QUICConnectionErrorUPtr -QUICNetVConnection::_state_connection_established_process_protected_packet(const QUICPacket &packet) +QUICNetVConnection::_state_connection_established_process_protected_packet(const QUICShortHeaderPacketR &packet) { QUICConnectionErrorUPtr error = nullptr; bool has_non_probing_frame = false; @@ -1204,16 +1280,10 @@ QUICNetVConnection::_state_connection_established_process_protected_packet(const return error; } - // Migrate connection if required - // FIXME Connection migration will be initiated when a peer sent non-probing frames. - // We need to two or more paths because we need to respond to probing packets on a new path and also need to send other frames - // on the old path until they initiate migration. - // if (packet.destination_cid() == this->_quic_connection_id && has_non_probing_frame) { + // Migrate connection if needed if (this->_alt_con_manager != nullptr) { - if (packet.destination_cid() != this->_quic_connection_id || !ats_ip_addr_port_eq(packet.from(), this->remote_addr)) { - if (!has_non_probing_frame) { - QUICConDebug("FIXME: Connection migration has been initiated without non-probing frames"); - } + if (has_non_probing_frame && + (packet.destination_cid() != this->_quic_connection_id || !ats_ip_addr_port_eq(packet.from(), this->remote_addr))) { error = this->_state_connection_established_migrate_connection(packet); if (error != nullptr) { return error; @@ -1238,7 +1308,8 @@ QUICNetVConnection::_state_connection_established_receive_packet() // Receive a QUIC packet net_activity(this, this_ethread()); do { - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::FAILED) { // Don't make this error, and discard the packet. // Because: @@ -1256,13 +1327,13 @@ QUICNetVConnection::_state_connection_established_receive_packet() // Process the packet switch (packet->type()) { case QUICPacketType::PROTECTED: - error = this->_state_connection_established_process_protected_packet(*packet); + error = this->_state_connection_established_process_protected_packet(static_cast(*packet)); break; case QUICPacketType::INITIAL: case QUICPacketType::HANDSHAKE: case QUICPacketType::ZERO_RTT_PROTECTED: // Pass packet to _recv_and_ack to send ack to the packet. Stream data will be discarded by offset mismatch. - error = this->_recv_and_ack(*packet); + error = this->_recv_and_ack(static_cast(*packet)); break; default: QUICConDebug("Unknown packet type: %s(%" PRIu8 ")", QUICDebugNames::packet_type(packet->type()), @@ -1281,14 +1352,15 @@ QUICNetVConnection::_state_closing_receive_packet() { while (this->_packet_recv_queue.size() > 0) { QUICPacketCreationResult result; - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::SUCCESS) { switch (packet->type()) { case QUICPacketType::VERSION_NEGOTIATION: // Ignore VN packets on closing state break; default: - this->_recv_and_ack(*packet); + this->_recv_and_ack(static_cast(*packet)); break; } } @@ -1312,9 +1384,10 @@ QUICNetVConnection::_state_draining_receive_packet() { while (this->_packet_recv_queue.size() > 0) { QUICPacketCreationResult result; - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::SUCCESS) { - this->_recv_and_ack(*packet); + this->_recv_and_ack(static_cast(*packet)); // Do NOT schedule WRITE_READY event from this point. // An endpoint in the draining state MUST NOT send any packets. } @@ -1348,25 +1421,34 @@ QUICNetVConnection::_state_common_send_packet() uint32_t written = 0; for (int i = static_cast(this->_minimum_encryption_level); i <= static_cast(QUICEncryptionLevel::ONE_RTT); ++i) { + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + auto level = QUIC_ENCRYPTION_LEVELS[i]; if (level == QUICEncryptionLevel::ONE_RTT && !this->_handshake_handler->is_completed()) { continue; } uint32_t max_packet_size = udp_payload_len - written; - if (this->netvc_context == NET_VCONNECTION_IN && !this->_verfied_state.is_verified()) { - max_packet_size = std::min(max_packet_size, this->_verfied_state.windows()); + if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { + max_packet_size = std::min(max_packet_size, this->_verified_state.windows()); } QUICPacketInfoUPtr packet_info = std::make_unique(); - QUICPacketUPtr packet = this->_packetize_frames(level, max_packet_size, packet_info->frames); + QUICPacketUPtr packet = this->_packetize_frames(packet_buf, level, max_packet_size, packet_info->frames); if (packet) { - packet_info->packet_number = packet->packet_number(); - packet_info->time_sent = Thread::get_hrtime(); - packet_info->ack_eliciting = packet->is_ack_eliciting(); - packet_info->is_crypto_packet = packet->is_crypto_packet(); - packet_info->in_flight = true; + // trigger callback + this->_context->trigger(QUICContext::CallbackEvent::PACKET_SEND, packet.get()); + + packet_info->packet_number = packet->packet_number(); + packet_info->time_sent = Thread::get_hrtime(); + packet_info->ack_eliciting = packet->is_ack_eliciting(); + 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; if (packet_info->ack_eliciting) { packet_info->sent_bytes = packet->size(); } else { @@ -1375,9 +1457,9 @@ QUICNetVConnection::_state_common_send_packet() packet_info->type = packet->type(); packet_info->pn_space = QUICTypeUtil::pn_space(level); - if (this->netvc_context == NET_VCONNECTION_IN && !this->_verfied_state.is_verified()) { - QUICConDebug("send to unverified window: %u", this->_verfied_state.windows()); - this->_verfied_state.consume(packet->size()); + if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { + QUICConDebug("send to unverified window: %u", this->_verified_state.windows()); + this->_verified_state.consume(packet->size()); } // TODO: do not write two QUIC Short Header Packets @@ -1415,6 +1497,9 @@ QUICNetVConnection::_state_common_send_packet() } if (packet_count) { + this->_context->trigger(QUICContext::CallbackEvent::METRICS_UPDATE, this->_congestion_controller->congestion_window(), + this->_congestion_controller->bytes_in_flight(), this->_congestion_controller->current_ssthresh()); + QUIC_INCREMENT_DYN_STAT_EX(QUICStats::total_packets_sent_stat, packet_count); net_activity(this, this_ethread()); } @@ -1447,11 +1532,9 @@ QUICNetVConnection::_store_frame(Ptr parent_block, size_t &size_a { Ptr new_block = frame.to_io_buffer_block(max_frame_size); - size_added = 0; - Ptr tmp = new_block; - while (tmp) { + size_added = 0; + for (Ptr tmp = new_block; tmp; tmp = tmp->next) { size_added += tmp->size(); - tmp = tmp->next; } if (parent_block == nullptr) { @@ -1480,7 +1563,8 @@ QUICNetVConnection::_store_frame(Ptr parent_block, size_t &size_a } QUICPacketUPtr -QUICNetVConnection::_packetize_frames(QUICEncryptionLevel level, uint64_t max_packet_size, std::vector &frames) +QUICNetVConnection::_packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel level, uint64_t max_packet_size, + std::vector &frames) { QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); if (max_packet_size <= MAX_PACKET_OVERHEAD) { @@ -1516,24 +1600,17 @@ QUICNetVConnection::_packetize_frames(QUICEncryptionLevel level, uint64_t max_pa break; } - if (g == this->_stream_manager) { - // Don't send DATA frames if current path is not validated - // FIXME will_generate_frame should receive more parameters so we don't need extra checks - if (auto path = this->_path_manager->get_verified_path(); !path.remote_ep().isValid()) { - break; - } - } - // Common block frame = g->generate_frame(frame_instance_buffer, level, this->_remote_flow_controller->credit(), max_frame_size, len, seq_num); if (frame) { + this->_context->trigger(QUICContext::CallbackEvent::FRAME_PACKETIZE, *frame); // Some frame types must not be sent on Initial and Handshake packets switch (auto t = frame->type(); level) { case QUICEncryptionLevel::INITIAL: case QUICEncryptionLevel::HANDSHAKE: ink_assert(t == QUICFrameType::CRYPTO || t == QUICFrameType::ACK || t == QUICFrameType::PADDING || - t == QUICFrameType::CONNECTION_CLOSE); + t == QUICFrameType::CONNECTION_CLOSE || t == QUICFrameType::PING); break; default: break; @@ -1576,7 +1653,7 @@ QUICNetVConnection::_packetize_frames(QUICEncryptionLevel level, uint64_t max_pa // Schedule a packet if (len != 0) { // Packet is retransmittable if it's not ack only packet - packet = this->_build_packet(level, first_block, ack_eliciting, probing, crypto); + packet = this->_build_packet(packet_buf, level, first_block, ack_eliciting, probing, crypto); } return packet; @@ -1600,22 +1677,30 @@ QUICNetVConnection::_packetize_closing_frame() size_t size_added = 0; uint64_t max_frame_size = static_cast(max_size); std::vector frames; - Ptr parent_block; - parent_block = nullptr; - parent_block = this->_store_frame(parent_block, size_added, max_frame_size, *frame, 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)); + first_block->fill(0); + last_block = this->_store_frame(last_block, size_added, max_frame_size, *frame, frames); QUICEncryptionLevel level = this->_hs_protocol->current_encryption_level(); ink_assert(level != QUICEncryptionLevel::ZERO_RTT); - this->_the_final_packet = this->_build_packet(level, parent_block, true, false, false); + this->_the_final_packet = this->_build_packet(this->_final_packet_buf, level, first_block, true, false, false); } QUICConnectionErrorUPtr -QUICNetVConnection::_recv_and_ack(const QUICPacket &packet, bool *has_non_probing_frame) +QUICNetVConnection::_recv_and_ack(const QUICPacketR &packet, bool *has_non_probing_frame) { ink_assert(packet.type() != QUICPacketType::RETRY); - const uint8_t *payload = packet.payload(); uint16_t size = packet.payload_length(); + ats_unique_buf payload_ubuf = ats_unique_malloc(size); + uint8_t *payload = payload_ubuf.get(); + size_t copied_len = 0; + for (auto b = packet.payload_block(); b; b = b->next) { + memcpy(payload + copied_len, b->start(), b->size()); + copied_len += b->size(); + } QUICPacketNumber packet_num = packet.packet_number(); QUICEncryptionLevel level = QUICTypeUtil::encryption_level(packet.type()); @@ -1627,8 +1712,10 @@ QUICNetVConnection::_recv_and_ack(const QUICPacket &packet, bool *has_non_probin *has_non_probing_frame = false; } - error = - this->_frame_dispatcher->receive_frames(level, payload, size, ack_only, is_flow_controlled, has_non_probing_frame, &packet); + error = this->_frame_dispatcher->receive_frames(*this->_context, level, payload, size, ack_only, is_flow_controlled, + has_non_probing_frame, static_cast(&packet)); + this->_context->trigger(QUICContext::CallbackEvent::PACKET_RECV, &packet); + if (error != nullptr) { return error; } @@ -1653,21 +1740,15 @@ QUICNetVConnection::_recv_and_ack(const QUICPacket &packet, bool *has_non_probin } QUICPacketUPtr -QUICNetVConnection::_build_packet(QUICEncryptionLevel level, Ptr parent_block, bool ack_eliciting, bool probing, - bool crypto) +QUICNetVConnection::_build_packet(uint8_t *packet_buf, QUICEncryptionLevel level, Ptr parent_block, + bool ack_eliciting, bool probing, bool crypto) { QUICPacketType type = QUICTypeUtil::packet_type(level); QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); - // FIXME Pass parent_block to create_x_packet - // No need to make a unique buf here - ats_unique_buf buf = ats_unique_malloc(2048); - uint8_t *raw_buf = buf.get(); - size_t len = 0; - while (parent_block) { - memcpy(raw_buf + len, parent_block->start(), parent_block->size()); - len += parent_block->size(); - parent_block = parent_block->next; + size_t len = 0; + for (Ptr tmp = parent_block; tmp; tmp = tmp->next) { + len += tmp->size(); } switch (type) { @@ -1688,26 +1769,26 @@ QUICNetVConnection::_build_packet(QUICEncryptionLevel level, Ptr } packet = this->_packet_factory.create_initial_packet( - dcid, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::INITIAL), std::move(buf), len, - ack_eliciting, probing, crypto, std::move(token), token_len); + packet_buf, dcid, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::INITIAL), parent_block, + len, ack_eliciting, probing, crypto, std::move(token), token_len); break; } case QUICPacketType::HANDSHAKE: { - packet = this->_packet_factory.create_handshake_packet(this->_peer_quic_connection_id, this->_quic_connection_id, + packet = this->_packet_factory.create_handshake_packet(packet_buf, this->_peer_quic_connection_id, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::HANDSHAKE), - std::move(buf), len, ack_eliciting, probing, crypto); + parent_block, len, ack_eliciting, probing, crypto); break; } case QUICPacketType::ZERO_RTT_PROTECTED: { - packet = this->_packet_factory.create_zero_rtt_packet(this->_original_quic_connection_id, this->_quic_connection_id, + packet = this->_packet_factory.create_zero_rtt_packet(packet_buf, this->_original_quic_connection_id, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::ZERO_RTT), - std::move(buf), len, ack_eliciting, probing); + parent_block, len, ack_eliciting, probing); break; } case QUICPacketType::PROTECTED: { - packet = this->_packet_factory.create_protected_packet(this->_peer_quic_connection_id, - this->_largest_acked_packet_number(QUICEncryptionLevel::ONE_RTT), - std::move(buf), len, ack_eliciting, probing); + packet = this->_packet_factory.create_short_header_packet(packet_buf, this->_peer_quic_connection_id, + this->_largest_acked_packet_number(QUICEncryptionLevel::ONE_RTT), + parent_block, len, ack_eliciting, probing); break; } default: @@ -1750,29 +1831,31 @@ QUICNetVConnection::_handle_error(QUICConnectionErrorUPtr error) static_cast(error->cls), QUICDebugNames::error_code(error->code), error->code); // Connection Error - this->close(std::move(error)); + this->close_quic_connection(std::move(error)); } QUICPacketUPtr -QUICNetVConnection::_dequeue_recv_packet(QUICPacketCreationResult &result) +QUICNetVConnection::_dequeue_recv_packet(uint8_t *packet_buf, QUICPacketCreationResult &result) { - QUICPacketUPtr packet = this->_packet_recv_queue.dequeue(result); + QUICPacketUPtr packet = this->_packet_recv_queue.dequeue(packet_buf, result); if (result == QUICPacketCreationResult::SUCCESS) { if (this->direction() == NET_VCONNECTION_OUT) { // Reset CID if a server sent back a new CID - // FIXME This should happen only once - QUICConnectionId src_cid = packet->source_cid(); - // FIXME src connection id could be zero ? if so, check packet header type. - if (src_cid != QUICConnectionId::ZERO()) { - if (this->_peer_quic_connection_id != src_cid) { - this->_update_peer_cid(src_cid); + // FIXME This should happen only once - it should probably be controlled by PathManager + if (packet->type() != QUICPacketType::PROTECTED && (packet->type() != QUICPacketType::RETRY || !this->_av_token)) { + QUICConnectionId src_cid = static_cast(*packet).source_cid(); + // FIXME src connection id could be zero ? if so, check packet header type. + if (src_cid != QUICConnectionId::ZERO()) { + if (this->_peer_quic_connection_id != src_cid) { + this->_update_peer_cid(src_cid); + } } } } - if (!this->_verfied_state.is_verified()) { - this->_verfied_state.fill(packet->size()); + if (!this->_verified_state.is_verified()) { + this->_verified_state.fill(packet->size()); } } @@ -1790,11 +1873,15 @@ QUICNetVConnection::_dequeue_recv_packet(QUICPacketCreationResult &result) QUICConDebug("Unsupported version"); break; case QUICPacketCreationResult::SUCCESS: - if (packet->type() == QUICPacketType::VERSION_NEGOTIATION) { + switch (packet->type()) { + case QUICPacketType::VERSION_NEGOTIATION: + case QUICPacketType::RETRY: QUICConDebug("[RX] %s packet size=%u", QUICDebugNames::packet_type(packet->type()), packet->size()); - } else { + 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()); + break; } break; default: @@ -1926,6 +2013,14 @@ QUICNetVConnection::_complete_handshake_if_possible() this->_handshake_handler->remote_transport_parameters()->getAsUInt(QUICTransportParameterId::ACK_DELAY_EXPONENT); this->_loss_detector->update_ack_delay_exponent(ack_delay_exponent); + const uint8_t *reset_token; + uint16_t reset_token_len; + reset_token = this->_handshake_handler->remote_transport_parameters()->getAsBytes(QUICTransportParameterId::STATELESS_RESET_TOKEN, + reset_token_len); + if (reset_token) { + this->_rtable->insert({reset_token}, this); + } + this->_start_application(); return 0; @@ -1974,12 +2069,16 @@ QUICNetVConnection::_switch_to_established_state() SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_connection_established); std::shared_ptr remote_tp = this->_handshake_handler->remote_transport_parameters(); + std::shared_ptr local_tp = this->_handshake_handler->local_transport_parameters(); uint64_t active_cid_limit = remote_tp->getAsUInt(QUICTransportParameterId::ACTIVE_CONNECTION_ID_LIMIT); if (active_cid_limit) { this->_alt_con_manager->set_remote_active_cid_limit(active_cid_limit); } + this->set_inactivity_timeout(HRTIME_MSECONDS(std::min(remote_tp->getAsUInt(QUICTransportParameterId::MAX_IDLE_TIMEOUT), + local_tp->getAsUInt(QUICTransportParameterId::MAX_IDLE_TIMEOUT)))); + if (this->direction() == NET_VCONNECTION_OUT) { uint16_t len; const uint8_t *pref_addr_buf = remote_tp->getAsBytes(QUICTransportParameterId::PREFERRED_ADDRESS, len); @@ -2100,12 +2199,7 @@ void QUICNetVConnection::_update_peer_cid(const QUICConnectionId &new_cid) { if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char old_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_peer_quic_connection_id.hex(old_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - new_cid.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("dcid: %s -> %s", old_cid_str, new_cid_str); + QUICConDebug("update peer dcid: %s -> %s", this->_peer_quic_connection_id.hex().c_str(), new_cid.hex().c_str()); } this->_peer_old_quic_connection_id = this->_peer_quic_connection_id; @@ -2117,12 +2211,7 @@ void QUICNetVConnection::_update_local_cid(const QUICConnectionId &new_cid) { if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char old_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_quic_connection_id.hex(old_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - new_cid.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("scid: %s -> %s", old_cid_str, new_cid_str); + QUICConDebug("update local dcid: %s -> %s", this->_quic_connection_id.hex().c_str(), new_cid.hex().c_str()); } this->_quic_connection_id = new_cid; @@ -2136,12 +2225,7 @@ QUICNetVConnection::_rerandomize_original_cid() this->_original_quic_connection_id.randomize(); if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char old_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - tmp.hex(old_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - this->_original_quic_connection_id.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("original cid: %s -> %s", old_cid_str, new_cid_str); + QUICConDebug("original cid: %s -> %s", tmp.hex().c_str(), this->_original_quic_connection_id.hex().c_str()); } } @@ -2153,12 +2237,13 @@ QUICNetVConnection::_setup_handshake_protocol(shared_SSL_CTX ctx) QUICTLS *tls = new QUICTLS(this->_pp_key_info, ctx.get(), this->direction(), this->options, this->_quic_config->client_session_file(), this->_quic_config->client_keylog_file()); SSL_set_ex_data(tls->ssl_handle(), QUIC::ssl_quic_qc_index, static_cast(this)); + TLSSessionResumptionSupport::bind(tls->ssl_handle(), this); return tls; } QUICConnectionErrorUPtr -QUICNetVConnection::_state_connection_established_migrate_connection(const QUICPacket &p) +QUICNetVConnection::_state_connection_established_migrate_connection(const QUICPacketR &p) { ink_assert(this->_handshake_handler->is_completed()); @@ -2208,9 +2293,7 @@ QUICNetVConnection::_state_connection_established_migrate_connection(const QUICP this->_validate_new_path(new_path); } } else { - char dcid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - dcid.hex(dcid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - QUICConDebug("Connection migration failed cid=%s", dcid_str); + QUICConDebug("Connection migration failed cid=%s", dcid.hex().c_str()); } } @@ -2265,3 +2348,9 @@ QUICNetVConnection::_handle_periodic_ack_event() this->_schedule_packet_write_ready(); } } + +const IpEndpoint & +QUICNetVConnection::_getLocalEndpoint() +{ + return local_addr; +} diff --git a/iocore/net/QUICPacketHandler.cc b/iocore/net/QUICPacketHandler.cc index 9afee71f4a0..a6e214d2b5f 100644 --- a/iocore/net/QUICPacketHandler.cc +++ b/iocore/net/QUICPacketHandler.cc @@ -22,6 +22,9 @@ #include "tscore/ink_config.h" #include "P_Net.h" +#include "P_QUICPacketHandler.h" +#include "P_QUICNetProcessor.h" +#include "P_QUICNet.h" #include "P_QUICClosedConCollector.h" #include "QUICGlobals.h" @@ -29,6 +32,10 @@ #include "QUICPacket.h" #include "QUICDebugNames.h" #include "QUICEvents.h" +#include "QUICResetTokenTable.h" + +#include "QUICMultiCertConfigLoader.h" +#include "QUICTLS.h" static constexpr char debug_tag[] = "quic_sec"; @@ -42,7 +49,7 @@ static constexpr char debug_tag[] = "quic_sec"; // // QUICPacketHandler // -QUICPacketHandler::QUICPacketHandler() +QUICPacketHandler::QUICPacketHandler(QUICResetTokenTable &rtable) : _rtable(rtable) { this->_closed_con_collector = new QUICClosedConCollector; this->_closed_con_collector->mutex = new_ProxyMutex(); @@ -118,11 +125,18 @@ QUICPacketHandler::_send_packet(UDPConnection *udp_con, IpEndpoint &addr, Ptr(udp_con)->ethread)->signalActivity(); } +QUICConnection * +QUICPacketHandler::_check_stateless_reset(const uint8_t *buf, size_t buf_len) +{ + return this->_rtable.lookup({buf + (buf_len - 16)}); +} + // // QUICPacketHandlerIn // -QUICPacketHandlerIn::QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable) - : NetAccept(opt), QUICPacketHandler(), _ctable(ctable) +QUICPacketHandlerIn::QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable, + QUICResetTokenTable &rtable) + : NetAccept(opt), QUICPacketHandler(rtable), _ctable(ctable) { this->mutex = new_ProxyMutex(); // create Connection Table @@ -141,7 +155,7 @@ NetAccept * QUICPacketHandlerIn::clone() const { NetAccept *na; - na = new QUICPacketHandlerIn(opt, this->_ctable); + na = new QUICPacketHandlerIn(opt, this->_ctable, this->_rtable); *na = *this; return na; } @@ -254,7 +268,7 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) } QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, buf, buf_len); + QUICLongHeaderPacketR::type(type, buf, buf_len); if (type == QUICPacketType::INITIAL) { // [draft-18] 7.2. // When an Initial packet is sent by a client which has not previously received a Retry packet from the server, it populates @@ -293,22 +307,33 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) // [draft-12] 6.1.2. Server Packet Handling // Servers MUST drop incoming packets under all other circumstances. They SHOULD send a Stateless Reset (Section 6.10.4) if a // connection ID is present in the header. - if ((!vc && !QUICInvariants::is_long_header(buf)) || (vc && vc->in_closed_queue)) { - if (is_debug_tag_set(debug_tag)) { - char dcid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - dcid.hex(dcid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); + if (!vc && !QUICInvariants::is_long_header(buf)) { + auto connection = static_cast(this->_check_stateless_reset(buf, buf_len)); + if (connection) { + QUICDebug("Stateless Reset has been received"); + connection->thread->schedule_imm(connection, QUIC_EVENT_STATELESS_RESET); + return; + } - if (!vc && !QUICInvariants::is_long_header(buf)) { - QUICDebugDS(scid, dcid, "sent Stateless Reset : connection not found, dcid=%s", dcid_str); - } else if (vc && vc->in_closed_queue) { - QUICDebugDS(scid, dcid, "sent Stateless Reset : connection is already closed, dcid=%s", dcid_str); - } + bool sent = + this->_send_stateless_reset(dcid, params->instance_id(), udp_packet->getConnection(), udp_packet->from, buf_len - 1); + 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()); } - QUICStatelessResetToken token(dcid, params->instance_id()); - auto packet = QUICPacketFactory::create_stateless_reset_packet(dcid, token); - this->_send_packet(*packet, udp_packet->getConnection(), udp_packet->from, 1200, nullptr, 0); + return; + + } else if (vc && vc->in_closed_queue) { + bool sent = + this->_send_stateless_reset(dcid, params->instance_id(), udp_packet->getConnection(), udp_packet->from, buf_len - 1); 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()); + } + return; } @@ -323,13 +348,11 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) QUICConnectionId peer_cid = scid; if (is_debug_tag_set("quic_sec")) { - char client_dcid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - original_cid.hex(client_dcid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - QUICDebugDS(peer_cid, original_cid, "client initial dcid=%s", client_dcid_hex_str); + QUICDebugDS(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->_ctable); + vc->init(peer_cid, original_cid, cid_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(); @@ -373,7 +396,7 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC QUICConnectionId dcid, QUICConnectionId scid, QUICConnectionId *original_cid) { QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, buf, buf_len); + QUICPacketR::type(type, buf, buf_len); if (type != QUICPacketType::INITIAL) { return 1; @@ -383,7 +406,7 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC size_t token_length = 0; uint8_t token_length_field_len = 0; size_t token_length_field_offset = 0; - if (!QUICPacketLongHeader::token_length(token_length, token_length_field_len, token_length_field_offset, buf, buf_len)) { + if (!QUICInitialPacketR::token_length(token_length, token_length_field_len, token_length_field_offset, buf, buf_len)) { return -1; } @@ -391,10 +414,11 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC QUICRetryToken token(from, dcid); QUICConnectionId local_cid; local_cid.randomize(); - QUICPacketUPtr retry_packet = QUICPacketFactory::create_retry_packet(scid, local_cid, dcid, token); + QUICPacketUPtr retry_packet = QUICPacketFactory::create_retry_packet(scid, local_cid, token); - QUICDebug("[TX] %s packet ODCID=%" PRIx64, QUICDebugNames::packet_type(retry_packet->type()), - static_cast(static_cast(retry_packet->header()).original_dcid())); + 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]); this->_send_packet(*retry_packet, connection, from, 1200, nullptr, 0); return -2; @@ -405,8 +429,13 @@ 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)); 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]); + this->_send_invalid_token_error(buf, buf_len, connection, from); return -3; } } else { @@ -418,10 +447,61 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC return 0; } +bool +QUICPacketHandlerIn::_send_stateless_reset(QUICConnectionId dcid, uint32_t instance_id, UDPConnection *udp_con, IpEndpoint &addr, + size_t maximum_size) +{ + QUICStatelessResetToken token(dcid, instance_id); + auto packet = QUICPacketFactory::create_stateless_reset_packet(token, maximum_size); + if (packet) { + this->_send_packet(*packet, udp_con, addr, 1200, nullptr, 0); + return true; + } + return false; +} + +void +QUICPacketHandlerIn::_send_invalid_token_error(const uint8_t *initial_packet, uint64_t initial_packet_len, + UDPConnection *connection, IpEndpoint from) +{ + QUICConnectionId scid_in_initial; + QUICConnectionId dcid_in_initial; + QUICInvariants::scid(scid_in_initial, initial_packet, initial_packet_len); + QUICInvariants::dcid(dcid_in_initial, initial_packet, initial_packet_len); + + // Create CONNECTION_CLOSE frame + auto error = std::make_unique(QUICTransErrorCode::INVALID_TOKEN); + uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + QUICFrame *frame = QUICFrameFactory::create_connection_close_frame(frame_buf, *error); + Ptr block = frame->to_io_buffer_block(1200); + size_t block_len = 0; + for (Ptr tmp = block; tmp; tmp = tmp->next) { + block_len += tmp->size(); + } + frame->~QUICFrame(); + + // Prepare for packet protection + QUICPacketProtectionKeyInfo ppki; + ppki.set_context(QUICPacketProtectionKeyInfo::Context::SERVER); + QUICPacketFactory pf(ppki); + 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); + + // Create INITIAL packet + QUICConnectionId scid; + scid.randomize(); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr cc_packet = pf.create_initial_packet(packet_buf, scid_in_initial, scid, 0, block, block_len, 0, 0, 1); + + this->_send_packet(*cc_packet, connection, from, 0, &php, scid_in_initial); +} + // // QUICPacketHandlerOut // -QUICPacketHandlerOut::QUICPacketHandlerOut() : Continuation(new_ProxyMutex()), QUICPacketHandler() +QUICPacketHandlerOut::QUICPacketHandlerOut(QUICResetTokenTable &rtable) : Continuation(new_ProxyMutex()), QUICPacketHandler(rtable) { SET_HANDLER(&QUICPacketHandlerOut::event_handler); } @@ -466,10 +546,11 @@ QUICPacketHandlerOut::_get_continuation() void QUICPacketHandlerOut::_recv_packet(int event, UDPPacket *udp_packet) { - if (is_debug_tag_set(debug_tag)) { - IOBufferBlock *block = udp_packet->getIOBlockChain(); - const uint8_t *buf = reinterpret_cast(block->buf()); + IOBufferBlock *block = udp_packet->getIOBlockChain(); + const uint8_t *buf = reinterpret_cast(block->buf()); + uint64_t buf_len = block->size(); + 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"), @@ -477,6 +558,24 @@ QUICPacketHandlerOut::_recv_packet(int event, UDPPacket *udp_packet) ats_ip_nptop(&udp_packet->to.sa, ipb_to, sizeof(ipb_to)), udp_packet->getPktLength()); } + QUICConnectionId dcid; + if (!QUICInvariants::dcid(dcid, buf, buf_len)) { + QUICDebug("Ignore packet - payload is too small"); + udp_packet->free(); + return; + } + + if (!QUICInvariants::is_long_header(buf) && dcid != this->_vc->connection_id()) { + auto connection = static_cast(this->_check_stateless_reset(buf, buf_len)); + if (connection) { + if (connection->connection_id() == this->_vc->connection_id()) { + QUICDebug("Stateless Reset has been received"); + this->_vc->thread->schedule_imm(this->_vc, QUIC_EVENT_STATELESS_RESET); + } + return; + } + } + this->_vc->handle_received_packet(udp_packet); eventProcessor.schedule_imm(this->_vc, ET_CALL, QUIC_EVENT_PACKET_READ_READY, nullptr); } diff --git a/iocore/net/quic/Makefile.am b/iocore/net/quic/Makefile.am index 8acdb53be75..75c4dafa168 100644 --- a/iocore/net/quic/Makefile.am +++ b/iocore/net/quic/Makefile.am @@ -30,7 +30,7 @@ AM_CPPFLAGS += \ -I$(abs_top_srcdir)/mgmt \ -I$(abs_top_srcdir)/mgmt/utils \ $(TS_INCLUDES) \ - @OPENSSL_INCLUDES@ + @OPENSSL_INCLUDES@ @YAMLCPP_INCLUDES@ noinst_LIBRARIES = libquic.a @@ -40,11 +40,20 @@ QUICPPProtector_impl = QUICPacketPayloadProtector_boringssl.cc QUICTLS_impl = QUICTLS_boringssl.cc QUICKeyGenerator_impl = QUICKeyGenerator_boringssl.cc else +if ENABLE_QUIC_OLD_API +QUICPHProtector_impl = QUICPacketHeaderProtector_legacy.cc +QUICPPProtector_impl = QUICPacketPayloadProtector_legacy.cc +QUICTLS_impl = QUICTLS_legacy.cc +QUICKeyGenerator_impl = QUICKeyGenerator_legacy.cc +else QUICPHProtector_impl = QUICPacketHeaderProtector_openssl.cc QUICPPProtector_impl = QUICPacketPayloadProtector_openssl.cc QUICTLS_impl = QUICTLS_openssl.cc QUICKeyGenerator_impl = QUICKeyGenerator_openssl.cc endif +endif + +QLog_impl = qlog/QLogEvent.cc qlog/QLogFrame.cc qlog/QLog.cc libquic_a_SOURCES = \ QUICGlobals.cc \ @@ -86,6 +95,8 @@ libquic_a_SOURCES = \ QUICPathManager.cc \ QUICPathValidator.cc \ QUICPinger.cc \ + QUICRetryIntegrityTag.cc \ + QUICResetTokenTable.cc \ QUICFrameGenerator.cc \ QUICFrameRetransmitter.cc \ QUICAddrVerifyState.cc \ @@ -95,7 +106,8 @@ libquic_a_SOURCES = \ QUICStreamFactory.cc \ QUICPadder.cc \ QUICContext.cc \ - QUICTokenCreator.cc + QUICTokenCreator.cc \ + $(QLog_impl) # # Check Programs diff --git a/iocore/net/quic/Mock.h b/iocore/net/quic/Mock.h index 0c78e30aa0c..68e421a9761 100644 --- a/iocore/net/quic/Mock.h +++ b/iocore/net/quic/Mock.h @@ -29,11 +29,15 @@ #include "QUICStreamManager.h" #include "QUICLossDetector.h" #include "QUICEvents.h" +#include "QUICPacketProtectionKeyInfo.h" +#include "QUICPinger.h" +#include "QUICPadder.h" +#include "QUICHandshakeProtocol.h" class MockQUICContext; using namespace std::literals; -std::string_view negotiated_application_name_sv = "h3-23"sv; +std::string_view negotiated_application_name_sv = "h3-27"sv; class MockQUICLDConfig : public QUICLDConfig { @@ -95,6 +99,35 @@ class MockQUICCCConfig : public QUICCCConfig } }; +class MockQUICPathManager : public QUICPathManager +{ +public: + virtual ~MockQUICPathManager() {} + virtual const QUICPath & + get_current_path() + { + return _path; + } + virtual const QUICPath & + get_verified_path() + { + return _path; + } + virtual void + open_new_path(const QUICPath &path, ink_hrtime timeout_in) + { + return; + } + virtual void + set_trusted_path(const QUICPath &path) + { + return; + } + +private: + QUICPath _path = {{}, {}}; +}; + class MockQUICConnectionInfoProvider : public QUICConnectionInfoProvider { QUICConnectionId @@ -169,7 +202,7 @@ class MockQUICConnectionInfoProvider : public QUICConnectionInfoProvider class MockQUICStreamManager : public QUICStreamManager { public: - MockQUICStreamManager(QUICConnectionInfoProvider *info) : QUICStreamManager(info, nullptr, nullptr) {} + MockQUICStreamManager(QUICContext *context) : QUICStreamManager(context, nullptr) {} // Override virtual QUICConnectionErrorUPtr @@ -349,7 +382,12 @@ class MockQUICConnection : public QUICConnection } void - close(QUICConnectionErrorUPtr error) override + close_quic_connection(QUICConnectionErrorUPtr error) override + { + } + + void + reset_quic_connection() override { } @@ -362,7 +400,7 @@ class MockQUICConnection : public QUICConnection QUICStreamManager * stream_manager() override { - return &_stream_manager; + return nullptr; } bool @@ -397,9 +435,8 @@ class MockQUICConnection : public QUICConnection int _transmit_count = 0; int _retransmit_count = 0; Ptr _mutex; - int _totalFrameCount = 0; - int _frameCount[256] = {0}; - MockQUICStreamManager _stream_manager = {this}; + int _totalFrameCount = 0; + int _frameCount[256] = {0}; QUICTransportParametersInEncryptedExtensions dummy_transport_parameters(); NetVConnectionContext_t _direction; @@ -443,6 +480,21 @@ class MockQUICCongestionController : public QUICCongestionController { return 0; } + virtual uint32_t + bytes_in_flight() const override + { + return 0; + } + virtual uint32_t + congestion_window() const override + { + return 0; + } + virtual uint32_t + current_ssthresh() const override + { + return 0; + } // for Test int @@ -498,15 +550,16 @@ class MockQUICPacketProtectionKeyInfo : public QUICPacketProtectionKeyInfo } }; -class MockQUICContext : public QUICContext, public QUICLDContext, public QUICCCContext +class MockQUICContext : public QUICContext { public: - MockQUICContext() + MockQUICContext() : QUICContext() { - _info = std::make_unique(); - _key_info = std::make_unique(); - _ld_config = std::make_unique(); - _cc_config = std::make_unique(); + _info = std::make_unique(); + _key_info = std::make_unique(); + _path_manager = std::make_unique(); + _ld_config = std::make_unique(); + _cc_config = std::make_unique(); } virtual QUICConnectionInfoProvider * @@ -543,6 +596,12 @@ class MockQUICContext : public QUICContext, public QUICLDContext, public QUICCCC return *_cc_config; } + virtual QUICPathManager * + path_manager() const override + { + return _path_manager.get(); + } + private: QUICConfig::scoped_config _config; QUICRTTMeasure _rtt_measure; @@ -550,6 +609,7 @@ class MockQUICContext : public QUICContext, public QUICLDContext, public QUICCCC std::unique_ptr _key_info; std::unique_ptr _ld_config; std::unique_ptr _cc_config; + std::unique_ptr _path_manager; }; class MockQUICLossDetector : public QUICLossDetector @@ -599,9 +659,29 @@ class MockQUICApplication : public QUICApplication } }; -class MockQUICPacket : public QUICPacket +class MockQUICPacketR : public QUICPacketR { public: + MockQUICPacketR() : QUICPacketR(nullptr, {}, {}) {} + + QUICPacketType + type() const override + { + return QUICPacketType::PROTECTED; + } + + QUICConnectionId + destination_cid() const override + { + return QUICConnectionId::ZERO(); + } + + QUICPacketNumber + packet_number() const override + { + return 0; + } + const IpEndpoint & from() const override { @@ -637,7 +717,7 @@ class MockQUICHandshakeProtocol : public QUICHandshakeProtocol MockQUICHandshakeProtocol(QUICPacketProtectionKeyInfo &pp_key_info) : QUICHandshakeProtocol(pp_key_info) {} int - handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) override + handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) override { return true; } diff --git a/iocore/net/quic/QUICAckFrameCreator.cc b/iocore/net/quic/QUICAckFrameCreator.cc index 98ca8983d7a..43167daad16 100644 --- a/iocore/net/quic/QUICAckFrameCreator.cc +++ b/iocore/net/quic/QUICAckFrameCreator.cc @@ -187,11 +187,8 @@ QUICAckFrameManager::QUICAckFrameCreator::push_back(QUICPacketNumber packet_numb this->_should_send = true; } - // every 2 full-packet should send a ack frame like tcp - this->_size_unsend += size; - // FIXME: this size should be fixed with PMTU - if (this->_size_unsend > 2 * 1480) { - this->_size_unsend = 0; + // every 2 ack-eliciting packet should send a ack frame + if (!ack_only && ++this->_ack_eliciting_count % 2 == 0) { this->_should_send = true; } @@ -224,7 +221,7 @@ QUICAckFrameManager::QUICAckFrameCreator::clear() this->_largest_ack_number = 0; this->_largest_ack_received_time = 0; this->_latest_packet_received_time = 0; - this->_size_unsend = 0; + this->_ack_eliciting_count = 0; this->_should_send = false; this->_available = false; } diff --git a/iocore/net/quic/QUICAckFrameCreator.h b/iocore/net/quic/QUICAckFrameCreator.h index 58e28e86868..42a7540bf71 100644 --- a/iocore/net/quic/QUICAckFrameCreator.h +++ b/iocore/net/quic/QUICAckFrameCreator.h @@ -69,7 +69,7 @@ class QUICAckFrameManager : public QUICFrameGenerator bool _available = false; // packet_number has data to sent bool _should_send = false; // ack frame should be sent immediately bool _has_new_data = false; // new data after last sent - size_t _size_unsend = 0; + uint32_t _ack_eliciting_count = 0; // every two ack-eliciting packet should send ack immediatly uint16_t _max_ack_delay = 25; QUICPacketNumber _largest_ack_number = 0; QUICPacketNumber _expect_next = 0; diff --git a/iocore/net/quic/QUICAltConnectionManager.cc b/iocore/net/quic/QUICAltConnectionManager.cc index 4e39e930244..569581caf5e 100644 --- a/iocore/net/quic/QUICAltConnectionManager.cc +++ b/iocore/net/quic/QUICAltConnectionManager.cc @@ -26,25 +26,28 @@ #include "tscore/ink_defs.h" #include "QUICAltConnectionManager.h" #include "QUICConnectionTable.h" +#include "QUICResetTokenTable.h" static constexpr char V_DEBUG_TAG[] = "v_quic_alt_con"; #define QUICACMVDebug(fmt, ...) Debug(V_DEBUG_TAG, "[%s] " fmt, this->_qc->cids().data(), ##__VA_ARGS__) -QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, +QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, QUICResetTokenTable &rtable, const QUICConnectionId &peer_initial_cid, uint32_t instance_id, uint8_t local_active_cid_limit, const IpEndpoint *preferred_endpoint_ipv4, const IpEndpoint *preferred_endpoint_ipv6) - : _qc(qc), _ctable(ctable), _instance_id(instance_id), _local_active_cid_limit(local_active_cid_limit) + : _qc(qc), _ctable(ctable), _rtable(rtable), _instance_id(instance_id), _local_active_cid_limit(local_active_cid_limit) { // Sequence number of the initial CID is 0 this->_alt_quic_connection_ids_remote.push_back({0, peer_initial_cid, {}, {true}}); + this->_alt_quic_connection_ids_local[0].seq_num = 0; + this->_alt_quic_connection_ids_local[0].advertised = true; if ((preferred_endpoint_ipv4 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv4, qc->five_tuple().source())) || (preferred_endpoint_ipv6 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv6, qc->five_tuple().source()))) { - this->_alt_quic_connection_ids_local[0] = this->_generate_next_alt_con_info(); + this->_alt_quic_connection_ids_local[1] = this->_generate_next_alt_con_info(); // This alt cid will be advertised via Transport Parameter, so no need to advertise it via NCID frame - this->_alt_quic_connection_ids_local[0].advertised = true; + this->_alt_quic_connection_ids_local[1].advertised = true; IpEndpoint empty_endpoint_ipv4; IpEndpoint empty_endpoint_ipv6; @@ -59,8 +62,8 @@ QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConne // FIXME Check nullptr dereference this->_local_preferred_address = - new QUICPreferredAddress(*preferred_endpoint_ipv4, *preferred_endpoint_ipv6, this->_alt_quic_connection_ids_local[0].id, - this->_alt_quic_connection_ids_local[0].token); + new QUICPreferredAddress(*preferred_endpoint_ipv4, *preferred_endpoint_ipv6, this->_alt_quic_connection_ids_local[1].id, + this->_alt_quic_connection_ids_local[1].token); } } @@ -116,9 +119,7 @@ QUICAltConnectionManager::_generate_next_alt_con_info() } if (is_debug_tag_set(V_DEBUG_TAG)) { - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - conn_id.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - QUICACMVDebug("alt-cid=%s", new_cid_str); + QUICACMVDebug("alt-cid=%s", conn_id.hex().c_str()); } return aci; @@ -127,7 +128,7 @@ QUICAltConnectionManager::_generate_next_alt_con_info() void QUICAltConnectionManager::_init_alt_connection_ids() { - for (int i = 0; i < this->_remote_active_cid_limit; ++i) { + for (int i = this->_alt_quic_connection_id_seq_num + 1; i < this->_remote_active_cid_limit; ++i) { this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info(); } this->_need_advertise = true; @@ -136,16 +137,6 @@ QUICAltConnectionManager::_init_alt_connection_ids() bool QUICAltConnectionManager::_update_alt_connection_id(uint64_t chosen_seq_num) { - // Seq 0 is special so it's not in the array - if (chosen_seq_num == 0) { - return true; - } - - // Seq 1 is for Preferred Address - if (chosen_seq_num == 1) { - return true; - } - for (int i = 0; i < this->_remote_active_cid_limit; ++i) { if (this->_alt_quic_connection_ids_local[i].seq_num == chosen_seq_num) { this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info(); @@ -166,10 +157,17 @@ QUICAltConnectionManager::_register_remote_connection_id(const QUICNewConnection error = std::make_unique(QUICTransErrorCode::PROTOCOL_VIOLATION, "received zero-length cid", QUICFrameType::NEW_CONNECTION_ID); } else { - int unused = std::count_if(this->_alt_quic_connection_ids_remote.begin(), this->_alt_quic_connection_ids_remote.end(), - [](AltConnectionInfo info) { return info.used == false && info.seq_num != 1; }); + int unused = 0; + for (auto &&x : this->_alt_quic_connection_ids_remote) { + if (x.seq_num == frame.sequence()) { + return error; + } + if (x.used == false && x.seq_num != 1) { + ++unused; + } + } if (unused > this->_local_active_cid_limit) { - error = std::make_unique(QUICTransErrorCode::PROTOCOL_VIOLATION, "received too many alt CIDs", + error = std::make_unique(QUICTransErrorCode::CONNECTION_ID_LIMIT_ERROR, "received too many alt CIDs", QUICFrameType::NEW_CONNECTION_ID); } else { this->_alt_quic_connection_ids_remote.push_back( @@ -215,6 +213,7 @@ QUICAltConnectionManager::migrate_to_alt_cid() continue; } info.used = true; + this->_rtable.insert(info.token, this->_qc); return info.id; } @@ -250,6 +249,7 @@ QUICAltConnectionManager::drop_cid(const QUICConnectionId &cid) if (it->id == cid) { QUICACMVDebug("Dropping advertized CID %" PRIx32 " seq# %" PRIu64, it->id.h32(), it->seq_num); this->_retired_seq_nums.push(it->seq_num); + this->_rtable.erase(it->token); this->_alt_quic_connection_ids_remote.erase(it); return; } diff --git a/iocore/net/quic/QUICAltConnectionManager.h b/iocore/net/quic/QUICAltConnectionManager.h index 09b99ca3d7c..f92e90e036a 100644 --- a/iocore/net/quic/QUICAltConnectionManager.h +++ b/iocore/net/quic/QUICAltConnectionManager.h @@ -31,12 +31,14 @@ #include "QUICConnection.h" class QUICConnectionTable; +class QUICResetTokenTable; class QUICAltConnectionManager : public QUICFrameHandler, public QUICFrameGenerator { public: - QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, const QUICConnectionId &peer_initial_cid, - uint32_t instance_id, uint8_t active_cid_limit, const IpEndpoint *preferred_endpoint_ipv4 = nullptr, + QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, QUICResetTokenTable &rtable, + const QUICConnectionId &peer_initial_cid, uint32_t instance_id, uint8_t active_cid_limit, + const IpEndpoint *preferred_endpoint_ipv4 = nullptr, const IpEndpoint *preferred_endpoint_ipv6 = nullptr); ~QUICAltConnectionManager(); @@ -95,6 +97,7 @@ class QUICAltConnectionManager : public QUICFrameHandler, public QUICFrameGenera QUICConnection *_qc = nullptr; QUICConnectionTable &_ctable; + QUICResetTokenTable &_rtable; AltConnectionInfo _alt_quic_connection_ids_local[8]; // 8 is perhaps enough std::vector _alt_quic_connection_ids_remote; std::queue _retired_seq_nums; diff --git a/iocore/net/quic/QUICConfig.cc b/iocore/net/quic/QUICConfig.cc index 1cfe703fc3f..cc44d007e79 100644 --- a/iocore/net/quic/QUICConfig.cc +++ b/iocore/net/quic/QUICConfig.cc @@ -49,10 +49,6 @@ quic_new_ssl_ctx() SSL_CTX_set_max_early_data(ssl_ctx, UINT32_C(0xFFFFFFFF)); - SSL_CTX_add_custom_ext(ssl_ctx, QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID, - SSL_EXT_TLS_ONLY | SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, - &QUICTransportParametersHandler::add, &QUICTransportParametersHandler::free, nullptr, - &QUICTransportParametersHandler::parse, nullptr); #else // QUIC Transport Parameters are accesible with SSL_set_quic_transport_params and SSL_get_peer_quic_transport_params #endif @@ -61,6 +57,11 @@ quic_new_ssl_ctx() // tatsuhiro-t's custom OpenSSL for QUIC draft-13 // https://github.com/tatsuhiro-t/openssl/tree/quic-draft-13 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_QUIC_HACK); + SSL_CTX_add_custom_ext(ssl_ctx, QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID, + SSL_EXT_TLS_ONLY | SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, + &QUICTransportParametersHandler::add, &QUICTransportParametersHandler::free, nullptr, + &QUICTransportParametersHandler::parse, nullptr); + #endif return ssl_ctx; @@ -120,11 +121,16 @@ QUICConfigParams::initialize() REC_EstablishStaticConfigInt32U(this->_stateless_retry, "proxy.config.quic.server.stateless_retry_enabled"); REC_EstablishStaticConfigInt32U(this->_vn_exercise_enabled, "proxy.config.quic.client.vn_exercise_enabled"); REC_EstablishStaticConfigInt32U(this->_cm_exercise_enabled, "proxy.config.quic.client.cm_exercise_enabled"); + REC_EstablishStaticConfigInt32U(this->_quantum_readiness_test_enabled_out, + "proxy.config.quic.client.quantum_readiness_test_enabled"); + REC_EstablishStaticConfigInt32U(this->_quantum_readiness_test_enabled_in, + "proxy.config.quic.server.quantum_readiness_test_enabled"); REC_ReadConfigStringAlloc(this->_server_supported_groups, "proxy.config.quic.server.supported_groups"); REC_ReadConfigStringAlloc(this->_client_supported_groups, "proxy.config.quic.client.supported_groups"); REC_ReadConfigStringAlloc(this->_client_session_file, "proxy.config.quic.client.session_file"); REC_ReadConfigStringAlloc(this->_client_keylog_file, "proxy.config.quic.client.keylog_file"); + REC_ReadConfigStringAlloc(this->_qlog_dir, "proxy.config.quic.qlog_dir"); // Transport Parameters REC_EstablishStaticConfigInt32U(this->_no_activity_timeout_in, "proxy.config.quic.no_activity_timeout_in"); @@ -159,6 +165,7 @@ QUICConfigParams::initialize() REC_EstablishStaticConfigInt32U(this->_max_ack_delay_out, "proxy.config.quic.max_ack_delay_out"); REC_EstablishStaticConfigInt32U(this->_active_cid_limit_in, "proxy.config.quic.active_cid_limit_in"); REC_EstablishStaticConfigInt32U(this->_active_cid_limit_out, "proxy.config.quic.active_cid_limit_out"); + REC_EstablishStaticConfigInt32U(this->_disable_active_migration, "proxy.config.quic.disable_active_migration"); // Loss Detection REC_EstablishStaticConfigInt32U(this->_ld_packet_threshold, "proxy.config.quic.loss_detection.packet_threshold"); @@ -244,6 +251,18 @@ QUICConfigParams::cm_exercise_enabled() const return this->_cm_exercise_enabled; } +uint32_t +QUICConfigParams::quantum_readiness_test_enabled_in() const +{ + return this->_quantum_readiness_test_enabled_in; +} + +uint32_t +QUICConfigParams::quantum_readiness_test_enabled_out() const +{ + return this->_quantum_readiness_test_enabled_out; +} + uint32_t QUICConfigParams::initial_max_data_in() const { @@ -352,6 +371,12 @@ QUICConfigParams::active_cid_limit_out() const return this->_active_cid_limit_out; } +bool +QUICConfigParams::disable_active_migration() const +{ + return this->_disable_active_migration; +} + const char * QUICConfigParams::server_supported_groups() const { @@ -447,6 +472,12 @@ QUICConfigParams::client_keylog_file() const return this->_client_keylog_file; } +const char * +QUICConfigParams::qlog_dir() const +{ + return this->_qlog_dir; +} + // // QUICConfig // diff --git a/iocore/net/quic/QUICConfig.h b/iocore/net/quic/QUICConfig.h index 67106e65ffb..38a655a8c14 100644 --- a/iocore/net/quic/QUICConfig.h +++ b/iocore/net/quic/QUICConfig.h @@ -40,11 +40,14 @@ class QUICConfigParams : public ConfigInfo uint32_t stateless_retry() const; uint32_t vn_exercise_enabled() const; uint32_t cm_exercise_enabled() const; + uint32_t quantum_readiness_test_enabled_in() const; + uint32_t quantum_readiness_test_enabled_out() const; const char *server_supported_groups() const; const char *client_supported_groups() const; const char *client_session_file() const; const char *client_keylog_file() const; + const char *qlog_dir() const; shared_SSL_CTX client_ssl_ctx() const; @@ -71,6 +74,7 @@ class QUICConfigParams : public ConfigInfo uint8_t max_ack_delay_out() const; uint8_t active_cid_limit_in() const; uint8_t active_cid_limit_out() const; + bool disable_active_migration() const; // Loss Detection uint32_t ld_packet_threshold() const; @@ -93,15 +97,18 @@ class QUICConfigParams : public ConfigInfo // TODO: make configurable static const uint8_t _scid_len = 18; //< Length of Source Connection ID - uint32_t _instance_id = 0; - uint32_t _stateless_retry = 0; - uint32_t _vn_exercise_enabled = 0; - uint32_t _cm_exercise_enabled = 0; + uint32_t _instance_id = 0; + uint32_t _stateless_retry = 0; + uint32_t _vn_exercise_enabled = 0; + uint32_t _cm_exercise_enabled = 0; + uint32_t _quantum_readiness_test_enabled_in = 0; + uint32_t _quantum_readiness_test_enabled_out = 0; char *_server_supported_groups = nullptr; char *_client_supported_groups = nullptr; char *_client_session_file = nullptr; char *_client_keylog_file = nullptr; + char *_qlog_dir = nullptr; shared_SSL_CTX _client_ssl_ctx = nullptr; @@ -130,6 +137,7 @@ class QUICConfigParams : public ConfigInfo uint32_t _max_ack_delay_out = 0; uint32_t _active_cid_limit_in = 0; uint32_t _active_cid_limit_out = 0; + uint32_t _disable_active_migration = 0; // [draft-17 recovery] 6.4.1. Constants of interest uint32_t _ld_packet_threshold = 3; diff --git a/iocore/net/quic/QUICCongestionController.h b/iocore/net/quic/QUICCongestionController.h index 46341dc2ba2..5afa8f01685 100644 --- a/iocore/net/quic/QUICCongestionController.h +++ b/iocore/net/quic/QUICCongestionController.h @@ -23,6 +23,8 @@ #pragma once +#include "QUICFrame.h" + struct QUICPacketInfo { // 6.3.1. Sent Packet Fields QUICPacketNumber packet_number; @@ -42,6 +44,13 @@ struct QUICPacketInfo { class QUICCongestionController { public: + enum class State : uint8_t { + RECOVERY, + CONGESTION_AVOIDANCE, + SLOW_START, + APPPLICATION_LIMITED, + }; + virtual ~QUICCongestionController() {} virtual void on_packet_sent(size_t bytes_sent) = 0; virtual void on_packet_acked(const QUICPacketInfo &acked_packet) = 0; @@ -50,4 +59,8 @@ class QUICCongestionController virtual void add_extra_credit() = 0; virtual void reset() = 0; virtual uint32_t credit() 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 28c5a83ec8c..ed75dc6f628 100644 --- a/iocore/net/quic/QUICConnection.h +++ b/iocore/net/quic/QUICConnection.h @@ -53,8 +53,9 @@ class QUICConnectionInfoProvider class QUICConnection : public QUICFrameHandler, public QUICConnectionInfoProvider { public: - virtual QUICStreamManager *stream_manager() = 0; - virtual void close(QUICConnectionErrorUPtr error) = 0; - virtual void handle_received_packet(UDPPacket *packeet) = 0; - virtual void ping() = 0; + virtual QUICStreamManager *stream_manager() = 0; + virtual void close_quic_connection(QUICConnectionErrorUPtr error) = 0; + virtual void reset_quic_connection() = 0; + virtual void handle_received_packet(UDPPacket *packeet) = 0; + virtual void ping() = 0; }; diff --git a/iocore/net/quic/QUICContext.cc b/iocore/net/quic/QUICContext.cc index 7ccb9e4043f..4d0d5019317 100644 --- a/iocore/net/quic/QUICContext.cc +++ b/iocore/net/quic/QUICContext.cc @@ -100,48 +100,55 @@ class QUICLDConfigQCP : public QUICLDConfig const QUICConfigParams *_params; }; -QUICContextImpl::QUICContextImpl(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, - QUICPacketProtectionKeyInfoProvider *key_info) +QUICContext::QUICContext(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, QUICPacketProtectionKeyInfoProvider *key_info, + QUICPathManager *path_manager) : _key_info(key_info), _connection_info(info), _rtt_provider(rtt), + _path_manager(path_manager), _ld_config(std::make_unique(_config)), _cc_config(std::make_unique(_config)) { } QUICConnectionInfoProvider * -QUICContextImpl::connection_info() const +QUICContext::connection_info() const { return _connection_info; } QUICConfig::scoped_config -QUICContextImpl::config() const +QUICContext::config() const { return _config; } QUICPacketProtectionKeyInfoProvider * -QUICContextImpl::key_info() const +QUICContext::key_info() const { return _key_info; } QUICRTTProvider * -QUICContextImpl::rtt_provider() const +QUICContext::rtt_provider() const { return _rtt_provider; } QUICLDConfig & -QUICContextImpl::ld_config() const +QUICContext::ld_config() const { return *_ld_config; } QUICCCConfig & -QUICContextImpl::cc_config() const +QUICContext::cc_config() const { return *_cc_config; } + +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 57e6c0b3a3b..31e494cc6d1 100644 --- a/iocore/net/quic/QUICContext.h +++ b/iocore/net/quic/QUICContext.h @@ -25,60 +25,170 @@ #include "QUICConnection.h" #include "QUICConfig.h" +#include "QUICEvents.h" +#include "QUICCongestionController.h" class QUICRTTProvider; class QUICCongestionController; class QUICPacketProtectionKeyInfoProvider; +class QUICPathManager; +class QUICPacketR; +class QUICPacket; class QUICNetVConnection; +struct QUICPacketInfo; -class QUICContext -{ -public: - virtual ~QUICContext(){}; - virtual QUICConnectionInfoProvider *connection_info() const = 0; - virtual QUICConfig::scoped_config config() const = 0; -}; - -class QUICLDContext +// this class is a connection between the callbacks. it should do something +// TODO: it should do something +class QUICCallbackContext { -public: - virtual ~QUICLDContext() {} - virtual QUICConnectionInfoProvider *connection_info() const = 0; - virtual QUICLDConfig &ld_config() const = 0; - virtual QUICPacketProtectionKeyInfoProvider *key_info() const = 0; }; -class QUICCCContext +class QUICCallback { public: - virtual ~QUICCCContext() {} - virtual QUICConnectionInfoProvider *connection_info() const = 0; - virtual QUICCCConfig &cc_config() const = 0; - virtual QUICRTTProvider *rtt_provider() const = 0; + virtual ~QUICCallback() {} + + // callback on connection close event + virtual void connection_close_callback(QUICCallbackContext &){}; + // 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){}; + // callback on packet receive event + virtual void packet_recv_callback(QUICCallbackContext &, const QUICPacket &p){}; + // callback on packet acked event + virtual void cc_metrics_update_callback(QUICCallbackContext &, uint64_t congestion_window, uint64_t bytes_in_flight, + uint64_t sshresh){}; + // callback on packet receive event + virtual void frame_packetize_callback(QUICCallbackContext &, const QUICFrame &p){}; + // callback on packet receive event + virtual void frame_recv_callback(QUICCallbackContext &, const QUICFrame &p){}; + // callback on packet receive event + virtual void congestion_state_updated_callback(QUICCallbackContext &, QUICCongestionController::State p){}; }; -class QUICContextImpl : public QUICContext, public QUICCCContext, public QUICLDContext +class QUICContext { public: - QUICContextImpl(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, QUICPacketProtectionKeyInfoProvider *key_info); - - virtual QUICConnectionInfoProvider *connection_info() const override; - virtual QUICConfig::scoped_config config() const override; - virtual QUICRTTProvider *rtt_provider() const override; + QUICContext(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, QUICPacketProtectionKeyInfoProvider *key_info, + QUICPathManager *path_manager); - // TODO should be more abstract - virtual QUICPacketProtectionKeyInfoProvider *key_info() const override; - - virtual QUICLDConfig &ld_config() const override; - virtual QUICCCConfig &cc_config() const override; + virtual ~QUICContext(){}; + virtual QUICConnectionInfoProvider *connection_info() const; + virtual QUICConfig::scoped_config config() const; + virtual QUICLDConfig &ld_config() const; + virtual QUICPacketProtectionKeyInfoProvider *key_info() const; + virtual QUICCCConfig &cc_config() const; + virtual QUICRTTProvider *rtt_provider() const; + virtual QUICPathManager *path_manager() const; + + // regist a callback which will be called when specifed event happen. + void + regist_callback(std::shared_ptr cbs) + { + this->_callbacks.push_back(cbs); + } + + enum class CallbackEvent : uint8_t { + PACKET_LOST, + PACKET_SEND, + FRAME_PACKETIZE, + PACKET_RECV, + FRAME_RECV, + METRICS_UPDATE, + CONNECTION_CLOSE, + CONGESTION_STATE_CHANGED, + }; + + // FIXME stupid trigger should be fix in more smart way. + void + trigger(CallbackEvent e, const QUICPacket *p = nullptr) + { + QUICCallbackContext ctx; + switch (e) { + case CallbackEvent::PACKET_RECV: + for (auto &&it : this->_callbacks) { + it->packet_recv_callback(ctx, *p); + } + break; + case CallbackEvent::PACKET_SEND: + for (auto &&it : this->_callbacks) { + it->packet_send_callback(ctx, *p); + } + break; + case CallbackEvent::CONNECTION_CLOSE: + for (auto &&it : this->_callbacks) { + it->connection_close_callback(ctx); + } + break; + default: + break; + } + } + + void + trigger(CallbackEvent e, const QUICPacketInfo &p) + { + QUICCallbackContext ctx; + for (auto &&it : this->_callbacks) { + it->packet_lost_callback(ctx, p); + } + } + + void + trigger(CallbackEvent e, uint64_t congestion_window, uint64_t bytes_in_flight, uint64_t sshresh) + { + QUICCallbackContext ctx; + for (auto &&it : this->_callbacks) { + it->cc_metrics_update_callback(ctx, congestion_window, bytes_in_flight, sshresh); + } + } + + void + trigger(CallbackEvent e, QUICCongestionController::State state) + { + QUICCallbackContext ctx; + for (auto &&it : this->_callbacks) { + it->congestion_state_updated_callback(ctx, state); + } + } + + void + trigger(CallbackEvent e, const QUICFrame &frame) + { + QUICCallbackContext ctx; + switch (e) { + case CallbackEvent::FRAME_PACKETIZE: + + for (auto &&it : this->_callbacks) { + it->frame_packetize_callback(ctx, frame); + } + break; + + case CallbackEvent::FRAME_RECV: + for (auto &&it : this->_callbacks) { + it->frame_recv_callback(ctx, frame); + } + break; + default: + break; + } + } + +protected: + // For Mock + QUICContext() {} private: QUICConfig::scoped_config _config; QUICPacketProtectionKeyInfoProvider *_key_info = nullptr; QUICConnectionInfoProvider *_connection_info = nullptr; QUICRTTProvider *_rtt_provider = nullptr; + QUICPathManager *_path_manager = nullptr; std::unique_ptr _ld_config = nullptr; std::unique_ptr _cc_config = nullptr; + + std::vector> _callbacks; }; diff --git a/iocore/net/quic/QUICDebugNames.cc b/iocore/net/quic/QUICDebugNames.cc index bf9d2d8f45a..6a75032cd18 100644 --- a/iocore/net/quic/QUICDebugNames.cc +++ b/iocore/net/quic/QUICDebugNames.cc @@ -90,6 +90,8 @@ QUICDebugNames::frame_type(QUICFrameType type) return "RETIRE_CONNECTION_ID"; case QUICFrameType::NEW_TOKEN: return "NEW_TOKEN"; + case QUICFrameType::HANDSHAKE_DONE: + return "HANDSHAKE_DONE"; case QUICFrameType::UNKNOWN: default: return "UNKNOWN"; @@ -131,8 +133,12 @@ QUICDebugNames::error_code(uint16_t code) return "FRAME_ENCODING_ERROR"; case static_cast(QUICTransErrorCode::TRANSPORT_PARAMETER_ERROR): return "TRANSPORT_PARAMETER_ERROR"; + case static_cast(QUICTransErrorCode::CONNECTION_ID_LIMIT_ERROR): + return "CONNECTION_ID_LIMIT_ERROR"; case static_cast(QUICTransErrorCode::PROTOCOL_VIOLATION): return "PROTOCOL_VIOLATION"; + case static_cast(QUICTransErrorCode::INVALID_TOKEN): + return "INVALID_TOKEN"; case static_cast(QUICTransErrorCode::CRYPTO_BUFFER_EXCEEDED): return "CRYPTO_BUFFER_EXCEEDED"; default: @@ -179,8 +185,8 @@ QUICDebugNames::transport_parameter_id(QUICTransportParameterId id) return "INITIAL_MAX_DATA"; case QUICTransportParameterId::INITIAL_MAX_STREAMS_BIDI: return "INITIAL_MAX_STREAMS_BIDI"; - case QUICTransportParameterId::IDLE_TIMEOUT: - return "IDLE_TIMEOUT"; + case QUICTransportParameterId::MAX_IDLE_TIMEOUT: + return "MAX_IDLE_TIMEOUT"; case QUICTransportParameterId::PREFERRED_ADDRESS: return "PREFERRED_ADDRESS"; case QUICTransportParameterId::MAX_PACKET_SIZE: diff --git a/iocore/net/quic/QUICEvents.h b/iocore/net/quic/QUICEvents.h index c758249ee93..50362e899a8 100644 --- a/iocore/net/quic/QUICEvents.h +++ b/iocore/net/quic/QUICEvents.h @@ -35,4 +35,5 @@ enum { QUIC_EVENT_ACK_PERIODIC, QUIC_EVENT_SHUTDOWN, QUIC_EVENT_LD_SHUTDOWN, + QUIC_EVENT_STATELESS_RESET, }; diff --git a/iocore/net/quic/QUICFlowController.cc b/iocore/net/quic/QUICFlowController.cc index b5275d0818b..60e665c6d1a 100644 --- a/iocore/net/quic/QUICFlowController.cc +++ b/iocore/net/quic/QUICFlowController.cc @@ -120,8 +120,17 @@ QUICFlowController::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint if (this->_should_create_frame) { frame = this->_create_frame(buf); - if (frame && frame->size() <= maximum_frame_size) { - this->_should_create_frame = false; + if (frame) { + if (frame->size() <= maximum_frame_size) { + this->_should_create_frame = false; + QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); + info->type = frame->type(); + info->level = QUICEncryptionLevel::NONE; + *(reinterpret_cast(info->data)) = this->_limit; + this->_records_frame(frame->id(), std::move(info)); + } else { + frame = nullptr; + } } } @@ -223,48 +232,23 @@ QUICLocalFlowController::_need_to_forward_limit() QUICFrame * QUICRemoteConnectionFlowController::_create_frame(uint8_t *buf) { - auto frame = QUICFrameFactory::create_data_blocked_frame(buf, this->_offset, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_offset; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_data_blocked_frame(buf, this->_offset, this->_issue_frame_id(), this); } QUICFrame * QUICLocalConnectionFlowController::_create_frame(uint8_t *buf) { - auto frame = QUICFrameFactory::create_max_data_frame(buf, this->_limit, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_limit; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_max_data_frame(buf, this->_limit, this->_issue_frame_id(), this); } QUICFrame * QUICRemoteStreamFlowController::_create_frame(uint8_t *buf) { - auto frame = - QUICFrameFactory::create_stream_data_blocked_frame(buf, this->_stream_id, this->_offset, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_offset; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_stream_data_blocked_frame(buf, this->_stream_id, this->_offset, this->_issue_frame_id(), this); } QUICFrame * QUICLocalStreamFlowController::_create_frame(uint8_t *buf) { - auto frame = QUICFrameFactory::create_max_stream_data_frame(buf, this->_stream_id, this->_limit, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_limit; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_max_stream_data_frame(buf, this->_stream_id, this->_limit, this->_issue_frame_id(), this); } diff --git a/iocore/net/quic/QUICFrame.cc b/iocore/net/quic/QUICFrame.cc index 68f03e300eb..1bffae34f36 100644 --- a/iocore/net/quic/QUICFrame.cc +++ b/iocore/net/quic/QUICFrame.cc @@ -46,7 +46,7 @@ read_varint(uint8_t *&pos, size_t len, uint64_t &field, size_t &field_len) return false; } - field = QUICIntUtil::read_QUICVariableInt(pos); + field = QUICIntUtil::read_QUICVariableInt(pos, len); pos += field_len; return true; } @@ -63,10 +63,10 @@ QUICFrame::ack_eliciting() const { auto type = this->type(); - return type != QUICFrameType::PADDING && type != QUICFrameType::ACK; + return type != QUICFrameType::PADDING && type != QUICFrameType::ACK && type != QUICFrameType::CONNECTION_CLOSE; } -const QUICPacket * +const QUICPacketR * QUICFrame::packet() const { return this->_packet; @@ -112,7 +112,7 @@ QUICFrame::type(const uint8_t *buf) buf[0] < static_cast(QUICFrameType::NEW_CONNECTION_ID)) { return QUICFrameType::STREAMS_BLOCKED; } else if (static_cast(QUICFrameType::CONNECTION_CLOSE) <= buf[0] && - buf[0] < static_cast(QUICFrameType::UNKNOWN)) { + buf[0] < static_cast(QUICFrameType::HANDSHAKE_DONE)) { return QUICFrameType::CONNECTION_CLOSE; } else { return static_cast(buf[0]); @@ -147,7 +147,7 @@ QUICStreamFrame::QUICStreamFrame(Ptr &block, QUICStreamId stream_ { } -QUICStreamFrame::QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICStreamFrame::QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -164,7 +164,7 @@ QUICStreamFrame::QUICStreamFrame(const QUICStreamFrame &o) } void -QUICStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -392,7 +392,7 @@ QUICCryptoFrame::QUICCryptoFrame(Ptr &block, QUICOffset offset, Q { } -QUICCryptoFrame::QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICCryptoFrame::QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -403,7 +403,7 @@ QUICCryptoFrame::QUICCryptoFrame(const QUICCryptoFrame &o) } void -QUICCryptoFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICCryptoFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -541,13 +541,29 @@ QUICCryptoFrame::data() const // ACK frame // -QUICAckFrame::QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +std::set +QUICAckFrame::ranges() const +{ + std::set numbers; + QUICPacketNumber x = this->largest_acknowledged(); + numbers.insert({x, static_cast(x) - this->ack_block_section()->first_ack_block()}); + x -= this->ack_block_section()->first_ack_block() + 1; + for (auto &&block : *(this->ack_block_section())) { + x -= block.gap() + 1; + numbers.insert({x, static_cast(x) - block.length()}); + x -= block.length() + 1; + } + + return numbers; +} + +QUICAckFrame::QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICAckFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICAckFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -997,13 +1013,13 @@ QUICRstStreamFrame::QUICRstStreamFrame(QUICStreamId stream_id, QUICAppErrorCode { } -QUICRstStreamFrame::QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICRstStreamFrame::QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICRstStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICRstStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1128,13 +1144,13 @@ QUICRstStreamFrame::final_offset() const // PING frame // -QUICPingFrame::QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICPingFrame::QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICPingFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPingFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { this->_reset(); this->_packet = packet; @@ -1179,13 +1195,13 @@ QUICPingFrame::to_io_buffer_block(size_t limit) const // // PADDING frame // -QUICPaddingFrame::QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICPaddingFrame::QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICPaddingFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPaddingFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1265,7 +1281,7 @@ QUICConnectionCloseFrame::QUICConnectionCloseFrame(uint64_t error_code, uint64_t { } -QUICConnectionCloseFrame::QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICConnectionCloseFrame::QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -1286,7 +1302,7 @@ QUICConnectionCloseFrame::_reset() } void -QUICConnectionCloseFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICConnectionCloseFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1488,13 +1504,13 @@ QUICMaxDataFrame::_reset() this->_size = 0; } -QUICMaxDataFrame::QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICMaxDataFrame::QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICMaxDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1588,14 +1604,14 @@ QUICMaxStreamDataFrame::_reset() this->_size = 0; } -QUICMaxStreamDataFrame::QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxStreamDataFrame::QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICMaxStreamDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxStreamDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1701,13 +1717,13 @@ QUICMaxStreamsFrame::_reset() this->_size = 0; } -QUICMaxStreamsFrame::QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICMaxStreamsFrame::QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICMaxStreamsFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxStreamsFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1775,7 +1791,8 @@ QUICMaxStreamsFrame::maximum_streams() const // // DATA_BLOCKED frame // -QUICDataBlockedFrame::QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICDataBlockedFrame::QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) + : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -1792,7 +1809,7 @@ QUICDataBlockedFrame::_reset() } void -QUICDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1866,7 +1883,7 @@ QUICDataBlockedFrame::offset() const // // STREAM_DATA_BLOCKED frame // -QUICStreamDataBlockedFrame::QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamDataBlockedFrame::QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -1885,7 +1902,7 @@ QUICStreamDataBlockedFrame::_reset() } void -QUICStreamDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1974,7 +1991,7 @@ QUICStreamDataBlockedFrame::offset() const // // STREAMS_BLOCKED frame // -QUICStreamIdBlockedFrame::QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamIdBlockedFrame::QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -1992,7 +2009,7 @@ QUICStreamIdBlockedFrame::_reset() } void -QUICStreamIdBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamIdBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2060,7 +2077,7 @@ QUICStreamIdBlockedFrame::stream_id() const // // NEW_CONNECTION_ID frame // -QUICNewConnectionIdFrame::QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICNewConnectionIdFrame::QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2080,7 +2097,7 @@ QUICNewConnectionIdFrame::_reset() } void -QUICNewConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICNewConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2190,11 +2207,10 @@ QUICNewConnectionIdFrame::to_io_buffer_block(size_t limit) const int QUICNewConnectionIdFrame::debug_msg(char *msg, size_t msg_len) const { - char cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->connection_id().hex(cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - return snprintf(msg, msg_len, "NEW_CONNECTION_ID size=%zu seq=%" PRIu64 " rpt=%" PRIu64 " cid=0x%s", this->size(), - this->sequence(), this->retire_prior_to(), cid_str); + return snprintf(msg, msg_len, "NEW_CONNECTION_ID size=%zu seq=%" PRIu64 " rpt=%" PRIu64 " cid=0x%s srt=%02x%02x%02x%02x", + this->size(), this->sequence(), this->retire_prior_to(), this->connection_id().hex().c_str(), + this->stateless_reset_token().buf()[0], this->stateless_reset_token().buf()[1], + this->stateless_reset_token().buf()[2], this->stateless_reset_token().buf()[3]); } uint64_t @@ -2243,13 +2259,14 @@ QUICStopSendingFrame::_reset() this->_size = 0; } -QUICStopSendingFrame::QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICStopSendingFrame::QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) + : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICStopSendingFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStopSendingFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2336,7 +2353,7 @@ QUICStopSendingFrame::stream_id() const // // PATH_CHALLENGE frame // -QUICPathChallengeFrame::QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathChallengeFrame::QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2353,7 +2370,7 @@ QUICPathChallengeFrame::_reset() } void -QUICPathChallengeFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathChallengeFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2435,7 +2452,7 @@ QUICPathChallengeFrame::data() const // // PATH_RESPONSE frame // -QUICPathResponseFrame::QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathResponseFrame::QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2478,7 +2495,7 @@ QUICPathResponseFrame::to_io_buffer_block(size_t limit) const } void -QUICPathResponseFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathResponseFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2530,7 +2547,7 @@ QUICPathResponseFrame::data() const // // QUICNewTokenFrame // -QUICNewTokenFrame::QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICNewTokenFrame::QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -2548,7 +2565,7 @@ QUICNewTokenFrame::_reset() } void -QUICNewTokenFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICNewTokenFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2632,7 +2649,7 @@ QUICNewTokenFrame::token() const // // RETIRE_CONNECTION_ID frame // -QUICRetireConnectionIdFrame::QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICRetireConnectionIdFrame::QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2651,7 +2668,7 @@ QUICRetireConnectionIdFrame::_reset() } void -QUICRetireConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICRetireConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2722,6 +2739,59 @@ QUICRetireConnectionIdFrame::seq_num() const return this->_seq_num; } +// +// HANDSHAKE_DONE frame +// + +QUICHandshakeDoneFrame::QUICHandshakeDoneFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) + : QUICFrame(0, nullptr, packet) +{ + this->parse(buf, len, packet); +} + +void +QUICHandshakeDoneFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) +{ + this->_reset(); + this->_packet = packet; + this->_valid = true; + this->_size = 1; +} + +QUICFrameType +QUICHandshakeDoneFrame::type() const +{ + return QUICFrameType::HANDSHAKE_DONE; +} + +size_t +QUICHandshakeDoneFrame::size() const +{ + return 1; +} + +Ptr +QUICHandshakeDoneFrame::to_io_buffer_block(size_t limit) const +{ + Ptr block; + size_t n = 0; + + if (limit < this->size()) { + return block; + } + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(this->size(), BUFFER_SIZE_INDEX_32K)); + uint8_t *block_start = reinterpret_cast(block->start()); + + // Type + block_start[0] = static_cast(QUICFrameType::HANDSHAKE_DONE); + n += 1; + + block->fill(n); + return block; +} + // // UNKNOWN // @@ -2746,7 +2816,7 @@ QUICUnknownFrame::to_io_buffer_block(size_t limit) const } void -QUICUnknownFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICUnknownFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { this->_packet = packet; } @@ -2762,7 +2832,7 @@ QUICUnknownFrame::debug_msg(char *msg, size_t msg_len) const // QUICFrame * -QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacket *packet) +QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacketR *packet) { switch (QUICFrame::type(src)) { case QUICFrameType::STREAM: @@ -2822,6 +2892,9 @@ QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUI case QUICFrameType::RETIRE_CONNECTION_ID: new (buf) QUICRetireConnectionIdFrame(src, len, packet); return reinterpret_cast(buf); + case QUICFrameType::HANDSHAKE_DONE: + new (buf) QUICHandshakeDoneFrame(src, len, packet); + return reinterpret_cast(buf); default: // Unknown frame Debug("quic_frame_factory", "Unknown frame type %x", src[0]); @@ -2830,7 +2903,7 @@ QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUI } const QUICFrame & -QUICFrameFactory::fast_create(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICFrameFactory::fast_create(const uint8_t *buf, size_t len, const QUICPacketR *packet) { if (QUICFrame::type(buf) == QUICFrameType::UNKNOWN) { return this->_unknown_frame; @@ -3024,6 +3097,13 @@ QUICFrameFactory::create_retire_connection_id_frame(uint8_t *buf, uint64_t seq_n return reinterpret_cast(buf); } +QUICHandshakeDoneFrame * +QUICFrameFactory::create_handshake_done_frame(uint8_t *buf, QUICFrameId id, QUICFrameGenerator *owner) +{ + new (buf) QUICHandshakeDoneFrame(id, owner); + return reinterpret_cast(buf); +} + QUICFrameId QUICFrameInfo::id() const { diff --git a/iocore/net/quic/QUICFrame.h b/iocore/net/quic/QUICFrame.h index 8d78510ee10..e933e68f53f 100644 --- a/iocore/net/quic/QUICFrame.h +++ b/iocore/net/quic/QUICFrame.h @@ -30,13 +30,14 @@ #include "I_IOBuffer.h" #include #include +#include #include "QUICTypes.h" class QUICFrame; class QUICStreamFrame; class QUICCryptoFrame; -class QUICPacket; +class QUICPacketR; class QUICFrameGenerator; using QUICFrameId = uint64_t; @@ -57,16 +58,16 @@ class QUICFrame virtual bool is_flow_controlled() const; virtual Ptr to_io_buffer_block(size_t limit) const = 0; virtual int debug_msg(char *msg, size_t msg_len) const; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet){}; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet){}; virtual QUICFrameGenerator *generated_by(); bool valid() const; bool ack_eliciting() const; - const QUICPacket *packet() const; + const QUICPacketR *packet() const; LINK(QUICFrame, link); protected: virtual void _reset(){}; - QUICFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr, const QUICPacket *packet = nullptr) + QUICFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr, const QUICPacketR *packet = nullptr) : _id(id), _owner(owner), _packet(packet) { } @@ -74,7 +75,7 @@ class QUICFrame bool _valid = false; QUICFrameId _id = 0; QUICFrameGenerator *_owner = nullptr; - const QUICPacket *_packet = nullptr; + const QUICPacketR *_packet = nullptr; }; // @@ -85,7 +86,7 @@ class QUICStreamFrame : public QUICFrame { public: QUICStreamFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStreamFrame(Ptr &block, QUICStreamId streamid, QUICOffset offset, bool last = false, bool has_offset_field = true, bool has_length_field = true, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); @@ -96,7 +97,7 @@ class QUICStreamFrame : public QUICFrame virtual bool is_flow_controlled() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICStreamId stream_id() const; QUICOffset offset() const; @@ -131,7 +132,7 @@ class QUICCryptoFrame : public QUICFrame { public: QUICCryptoFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICCryptoFrame(Ptr &block, QUICOffset offset, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); QUICCryptoFrame(const QUICCryptoFrame &o); @@ -139,7 +140,7 @@ class QUICCryptoFrame : public QUICFrame virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICOffset offset() const; uint64_t data_length() const; @@ -265,9 +266,10 @@ class QUICAckFrame : public QUICFrame }; QUICAckFrame(QUICFrameId id = 0) : QUICFrame(id) {} - QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICAckFrame(QUICPacketNumber largest_acknowledged, uint64_t ack_delay, uint64_t first_ack_block, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); + std::set ranges() const; // There's no reasont restrict copy, but we need to write the copy constructor. Otherwise it will crash on destruct. QUICAckFrame(const QUICAckFrame &) = delete; @@ -276,7 +278,7 @@ class QUICAckFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; QUICPacketNumber largest_acknowledged() const; @@ -304,7 +306,7 @@ class QUICRstStreamFrame : public QUICFrame { public: QUICRstStreamFrame(QUICFrameId id = 0) : QUICFrame(id) {} - QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICRstStreamFrame(QUICStreamId stream_id, QUICAppErrorCode error_code, QUICOffset final_offset, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); @@ -312,7 +314,7 @@ class QUICRstStreamFrame : public QUICFrame virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICStreamId stream_id() const; QUICAppErrorCode error_code() const; @@ -334,11 +336,11 @@ class QUICPingFrame : public QUICFrame { public: QUICPingFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; private: }; @@ -351,12 +353,12 @@ class QUICPaddingFrame : public QUICFrame { public: QUICPaddingFrame(size_t size) : _size(size) {} - QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual bool is_probing_frame() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; private: // padding frame is a resident of padding frames @@ -372,7 +374,7 @@ class QUICConnectionCloseFrame : public QUICFrame { public: QUICConnectionCloseFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); // Constructor for transport error codes QUICConnectionCloseFrame(uint64_t error_code, QUICFrameType frame_type, uint64_t reason_phrase_length, const char *reason_phrase, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); @@ -383,7 +385,7 @@ class QUICConnectionCloseFrame : public QUICFrame virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint16_t error_code() const; QUICFrameType frame_type() const; @@ -408,13 +410,13 @@ class QUICMaxDataFrame : public QUICFrame { public: QUICMaxDataFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICMaxDataFrame(uint64_t maximum_data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint64_t maximum_data() const; @@ -432,12 +434,12 @@ class QUICMaxStreamDataFrame : public QUICFrame { public: QUICMaxStreamDataFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICMaxStreamDataFrame(QUICStreamId stream_id, uint64_t maximum_stream_data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; @@ -459,12 +461,12 @@ class QUICMaxStreamsFrame : public QUICFrame { public: QUICMaxStreamsFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICMaxStreamsFrame(QUICStreamId maximum_streams, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint64_t maximum_streams() const; private: @@ -480,13 +482,13 @@ class QUICDataBlockedFrame : public QUICFrame { public: QUICDataBlockedFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICDataBlockedFrame(QUICOffset offset, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _offset(offset){}; virtual QUICFrameType type() const override; virtual size_t size() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; virtual Ptr to_io_buffer_block(size_t limit) const override; @@ -506,14 +508,14 @@ class QUICStreamDataBlockedFrame : public QUICFrame { public: QUICStreamDataBlockedFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStreamDataBlockedFrame(QUICStreamId s, QUICOffset o, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _stream_id(s), _offset(o){}; virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; QUICStreamId stream_id() const; @@ -533,7 +535,7 @@ class QUICStreamIdBlockedFrame : public QUICFrame { public: QUICStreamIdBlockedFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStreamIdBlockedFrame(QUICStreamId s, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _stream_id(s) { @@ -541,7 +543,7 @@ class QUICStreamIdBlockedFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICStreamId stream_id() const; @@ -559,7 +561,7 @@ class QUICNewConnectionIdFrame : public QUICFrame { public: QUICNewConnectionIdFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICNewConnectionIdFrame(uint64_t seq, uint64_t ret, const QUICConnectionId &cid, QUICStatelessResetToken token, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _sequence(seq), _retire_prior_to(ret), _connection_id(cid), _stateless_reset_token(token){}; @@ -567,7 +569,7 @@ class QUICNewConnectionIdFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; uint64_t sequence() const; @@ -592,13 +594,13 @@ class QUICStopSendingFrame : public QUICFrame { public: QUICStopSendingFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStopSendingFrame(QUICStreamId stream_id, QUICAppErrorCode error_code, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; QUICStreamId stream_id() const; @@ -620,7 +622,7 @@ class QUICPathChallengeFrame : public QUICFrame public: static constexpr uint8_t DATA_LEN = 8; QUICPathChallengeFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICPathChallengeFrame(ats_unique_buf data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _data(std::move(data)) { @@ -628,7 +630,7 @@ class QUICPathChallengeFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual bool is_probing_frame() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; @@ -649,7 +651,7 @@ class QUICPathResponseFrame : public QUICFrame public: static constexpr uint8_t DATA_LEN = 8; QUICPathResponseFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICPathResponseFrame(ats_unique_buf data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _data(std::move(data)) { @@ -657,7 +659,7 @@ class QUICPathResponseFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual bool is_probing_frame() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; @@ -677,7 +679,7 @@ class QUICNewTokenFrame : public QUICFrame { public: QUICNewTokenFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICNewTokenFrame(ats_unique_buf token, size_t token_length, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _token_length(token_length), _token(std::move(token)) { @@ -685,7 +687,7 @@ class QUICNewTokenFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint64_t token_length() const; const uint8_t *token() const; @@ -705,7 +707,7 @@ class QUICRetireConnectionIdFrame : public QUICFrame { public: QUICRetireConnectionIdFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICRetireConnectionIdFrame(uint64_t seq_num, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _seq_num(seq_num) { @@ -713,7 +715,7 @@ class QUICRetireConnectionIdFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; uint64_t seq_num() const; @@ -724,16 +726,32 @@ class QUICRetireConnectionIdFrame : public QUICFrame uint64_t _seq_num = 0; }; +// +// HANDSHAKE_DONE +// + +class QUICHandshakeDoneFrame : public QUICFrame +{ +public: + QUICHandshakeDoneFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} + QUICHandshakeDoneFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); + virtual QUICFrameType type() const override; + virtual size_t size() const override; + virtual Ptr to_io_buffer_block(size_t limit) const override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; +}; + // // UNKNOWN // class QUICUnknownFrame : public QUICFrame { +public: QUICFrameType type() const override; size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; int debug_msg(char *msg, size_t msg_len) const override; }; @@ -746,13 +764,13 @@ class QUICFrameFactory /* * This is used for creating a QUICFrame object based on received data. */ - static QUICFrame *create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacket *packet); + static QUICFrame *create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacketR *packet); /* * This works almost the same as create() but it reuses created objects for performance. * If you create a frame object which has the same frame type that you created before, the object will be reset by new data. */ - const QUICFrame &fast_create(const uint8_t *buf, size_t len, const QUICPacket *packet); + const QUICFrame &fast_create(const uint8_t *buf, size_t len, const QUICPacketR *packet); /* * Creates a STREAM frame. @@ -879,6 +897,11 @@ class QUICFrameFactory */ static QUICPaddingFrame *create_padding_frame(uint8_t *buf, size_t size, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); + /* + * Creates a HANDSHAKE_DONE frame + */ + static QUICHandshakeDoneFrame *create_handshake_done_frame(uint8_t *buf, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); + private: // FIXME Actual number of frame types is several but some of the values are not sequential. QUICFrame *_reusable_frames[256] = {nullptr}; diff --git a/iocore/net/quic/QUICFrameDispatcher.cc b/iocore/net/quic/QUICFrameDispatcher.cc index b807ff516e0..95099332bc1 100644 --- a/iocore/net/quic/QUICFrameDispatcher.cc +++ b/iocore/net/quic/QUICFrameDispatcher.cc @@ -42,8 +42,9 @@ QUICFrameDispatcher::add_handler(QUICFrameHandler *handler) } QUICConnectionErrorUPtr -QUICFrameDispatcher::receive_frames(QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, bool &ack_only, - bool &is_flow_controlled, bool *has_non_probing_frame, const QUICPacket *packet) +QUICFrameDispatcher::receive_frames(QUICContext &ctx, QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, + bool &ack_only, bool &is_flow_controlled, bool *has_non_probing_frame, + const QUICPacketR *packet) { uint16_t cursor = 0; ack_only = true; @@ -63,6 +64,8 @@ QUICFrameDispatcher::receive_frames(QUICEncryptionLevel level, const uint8_t *pa QUICFrameType type = frame.type(); + ctx.trigger(QUICContext::CallbackEvent::FRAME_RECV, frame); + if (type == QUICFrameType::STREAM) { is_flow_controlled = true; } diff --git a/iocore/net/quic/QUICFrameDispatcher.h b/iocore/net/quic/QUICFrameDispatcher.h index e7ce785b49e..435103badc2 100644 --- a/iocore/net/quic/QUICFrameDispatcher.h +++ b/iocore/net/quic/QUICFrameDispatcher.h @@ -28,15 +28,16 @@ #include "QUICConnection.h" #include "QUICFrame.h" #include "QUICFrameHandler.h" +#include "QUICContext.h" class QUICFrameDispatcher { public: QUICFrameDispatcher(QUICConnectionInfoProvider *info); - QUICConnectionErrorUPtr receive_frames(QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, + QUICConnectionErrorUPtr receive_frames(QUICContext &context, QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, bool &should_send_ackbool, bool &is_flow_controlled, bool *has_non_probing_frame, - const QUICPacket *packet); + const QUICPacketR *packet); void add_handler(QUICFrameHandler *handler); diff --git a/iocore/net/quic/QUICHandshake.cc b/iocore/net/quic/QUICHandshake.cc index c88377f16dd..08c30619640 100644 --- a/iocore/net/quic/QUICHandshake.cc +++ b/iocore/net/quic/QUICHandshake.cc @@ -27,6 +27,7 @@ #include "QUICEvents.h" #include "QUICGlobals.h" +#include "QUICHandshakeProtocol.h" #include "QUICPacketFactory.h" #include "QUICVersionNegotiator.h" #include "QUICConfig.h" @@ -80,8 +81,6 @@ } static constexpr int UDP_MAXIMUM_PAYLOAD_SIZE = 65527; -// TODO: fix size -static constexpr int MAX_HANDSHAKE_MSG_LEN = 65527; QUICHandshake::QUICHandshake(QUICConnection *qc, QUICHandshakeProtocol *hsp) : QUICHandshake(qc, hsp, {}, false) {} @@ -119,7 +118,7 @@ QUICHandshake::start(const QUICTPConfig &tp_config, QUICPacketFactory *packet_fa } QUICConnectionErrorUPtr -QUICHandshake::start(const QUICTPConfig &tp_config, const QUICPacket &initial_packet, QUICPacketFactory *packet_factory, +QUICHandshake::start(const QUICTPConfig &tp_config, const QUICInitialPacketR &initial_packet, QUICPacketFactory *packet_factory, const QUICPreferredAddress *pref_addr) { // Negotiate version @@ -143,7 +142,7 @@ QUICHandshake::start(const QUICTPConfig &tp_config, const QUICPacket &initial_pa } QUICConnectionErrorUPtr -QUICHandshake::negotiate_version(const QUICPacket &vn, QUICPacketFactory *packet_factory) +QUICHandshake::negotiate_version(const QUICVersionNegotiationPacketR &vn, QUICPacketFactory *packet_factory) { // Client side only ink_assert(this->_qc->direction() == NET_VCONNECTION_OUT); @@ -185,6 +184,16 @@ QUICHandshake::is_completed() const return this->_hs_protocol->is_handshake_finished(); } +bool +QUICHandshake::is_confirmed() const +{ + if (this->_qc->direction() == NET_VCONNECTION_IN) { + return this->is_completed(); + } else { + return this->_is_handshake_done_received; + } +} + bool QUICHandshake::is_stateless_retry_enabled() const { @@ -298,6 +307,7 @@ QUICHandshake::interests() { return { QUICFrameType::CRYPTO, + QUICFrameType::HANDSHAKE_DONE, }; } @@ -308,8 +318,15 @@ QUICHandshake::handle_frame(QUICEncryptionLevel level, const QUICFrame &frame) switch (frame.type()) { case QUICFrameType::CRYPTO: error = this->_crypto_streams[static_cast(level)].recv(static_cast(frame)); - if (error != nullptr) { - return error; + if (error == nullptr) { + error = this->do_handshake(); + } + break; + case QUICFrameType::HANDSHAKE_DONE: + if (this->_qc->direction() == NET_VCONNECTION_IN) { + error = std::make_unique(QUICTransErrorCode::PROTOCOL_VIOLATION); + } else { + this->_is_handshake_done_received = true; } break; default: @@ -318,7 +335,7 @@ QUICHandshake::handle_frame(QUICEncryptionLevel level, const QUICFrame &frame) break; } - return this->do_handshake(); + return error; } bool @@ -328,7 +345,8 @@ QUICHandshake::will_generate_frame(QUICEncryptionLevel level, size_t current_pac return false; } - return this->_crypto_streams[static_cast(level)].will_generate_frame(level, current_packet_size, ack_eliciting, seq_num); + return (this->_qc->direction() == NET_VCONNECTION_IN && !this->_is_handshake_done_sent) || + this->_crypto_streams[static_cast(level)].will_generate_frame(level, current_packet_size, ack_eliciting, seq_num); } QUICFrame * @@ -338,8 +356,23 @@ QUICHandshake::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t QUICFrame *frame = nullptr; if (this->_is_level_matched(level)) { + // CRYPTO frame = this->_crypto_streams[static_cast(level)].generate_frame(buf, level, connection_credit, maximum_frame_size, current_packet_size, seq_num); + if (frame) { + return frame; + } + } + + if (level == QUICEncryptionLevel::ONE_RTT) { + // HANDSHAKE_DONE + if (!this->_is_handshake_done_sent && this->is_completed()) { + frame = QUICFrameFactory::create_handshake_done_frame(buf, this->_issue_frame_id(), this); + } + if (frame) { + this->_is_handshake_done_sent = true; + return frame; + } } return frame; @@ -351,7 +384,7 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co QUICTransportParametersInEncryptedExtensions *tp = new QUICTransportParametersInEncryptedExtensions(); // MUSTs - tp->set(QUICTransportParameterId::IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); + 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(), this->_qc->first_connection_id().length()); @@ -376,6 +409,9 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co if (tp_config.initial_max_stream_data_uni() != 0) { tp->set(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_UNI, tp_config.initial_max_stream_data_uni()); } + if (tp_config.disable_active_migration()) { + tp->set(QUICTransportParameterId::DISABLE_ACTIVE_MIGRATION, nullptr, 0); + } if (pref_addr != nullptr) { uint8_t pref_addr_buf[QUICPreferredAddress::MAX_LEN]; uint16_t len; @@ -392,6 +428,11 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co 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); + } + this->_local_transport_parameters = std::shared_ptr(tp); this->_hs_protocol->set_local_transport_parameters(this->_local_transport_parameters); } @@ -402,7 +443,7 @@ QUICHandshake::_load_local_client_transport_parameters(const QUICTPConfig &tp_co QUICTransportParametersInClientHello *tp = new QUICTransportParametersInClientHello(); // MUSTs - tp->set(QUICTransportParameterId::IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); + tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); // MAYs if (tp_config.initial_max_data() != 0) { @@ -428,6 +469,11 @@ QUICHandshake::_load_local_client_transport_parameters(const QUICTPConfig &tp_co tp->set(QUICTransportParameterId::ACTIVE_CONNECTION_ID_LIMIT, tp_config.active_cid_limit()); } + // Additional parameters + for (auto &¶m : tp_config.additional_tp()) { + tp->set(param.first, param.second.first, param.second.second); + } + this->_local_transport_parameters = std::shared_ptr(tp); this->_hs_protocol->set_local_transport_parameters(std::unique_ptr(tp)); } @@ -452,18 +498,13 @@ QUICHandshake::do_handshake() // TODO: check size if (bytes_avail > 0) { stream->read(in.buf + in.offsets[index], bytes_avail); - in.offsets[index] = bytes_avail; - in.offsets[4] += bytes_avail; } + in.offsets[index + 1] = in.offsets[index] + bytes_avail; } } - QUICHandshakeMsgs out; - uint8_t out_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - out.buf = out_buf; - out.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - int result = this->_hs_protocol->handshake(&out, &in); + QUICHandshakeMsgs *out = nullptr; + int result = this->_hs_protocol->handshake(&out, &in); if (this->_remote_transport_parameters == nullptr) { if (!this->check_remote_transport_parameters()) { result = 0; @@ -471,18 +512,25 @@ QUICHandshake::do_handshake() } if (result == 1) { - for (auto level : QUIC_ENCRYPTION_LEVELS) { - int index = static_cast(level); - QUICCryptoStream *stream = &this->_crypto_streams[index]; - size_t len = out.offsets[index + 1] - out.offsets[index]; - // TODO: check size - if (len > 0) { - stream->write(out.buf + out.offsets[index], len); + if (out) { + for (auto level : QUIC_ENCRYPTION_LEVELS) { + int index = static_cast(level); + QUICCryptoStream *stream = &this->_crypto_streams[index]; + size_t len = out->offsets[index + 1] - out->offsets[index]; + // TODO: check size + if (len > 0) { + stream->write(out->buf + out->offsets[index], len); + } } } - } else if (out.error_code != 0) { + } else { this->_hs_protocol->abort_handshake(); - error = std::make_unique(QUICErrorClass::TRANSPORT, out.error_code); + if (this->_hs_protocol->has_crypto_error()) { + error = std::make_unique(QUICErrorClass::TRANSPORT, this->_hs_protocol->crypto_error()); + } else { + error = std::make_unique(QUICErrorClass::TRANSPORT, + static_cast(QUICTransErrorCode::PROTOCOL_VIOLATION)); + } } return error; @@ -495,7 +543,7 @@ QUICHandshake::_abort_handshake(QUICTransErrorCode code) this->_hs_protocol->abort_handshake(); - this->_qc->close(QUICConnectionErrorUPtr(new QUICConnectionError(code))); + this->_qc->close_quic_connection(QUICConnectionErrorUPtr(new QUICConnectionError(code))); } /* @@ -514,3 +562,10 @@ QUICHandshake::_is_level_matched(QUICEncryptionLevel level) { return true; } + +void +QUICHandshake::_on_frame_lost(QUICFrameInformationUPtr &info) +{ + ink_assert(info->type == QUICFrameType::HANDSHAKE_DONE); + this->_is_handshake_done_sent = false; +} diff --git a/iocore/net/quic/QUICHandshake.h b/iocore/net/quic/QUICHandshake.h index 49ef4379ff2..5af42d8501a 100644 --- a/iocore/net/quic/QUICHandshake.h +++ b/iocore/net/quic/QUICHandshake.h @@ -35,6 +35,7 @@ */ class QUICVersionNegotiator; class QUICPacketFactory; +class QUICHandshakeProtocol; class SSLNextProtocolSet; class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator @@ -57,12 +58,12 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator // for client side QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, QUICPacketFactory *packet_factory, bool vn_exercise_enabled); - QUICConnectionErrorUPtr negotiate_version(const QUICPacket &packet, QUICPacketFactory *packet_factory); + QUICConnectionErrorUPtr negotiate_version(const QUICVersionNegotiationPacketR &packet, QUICPacketFactory *packet_factory); void reset(); // for server side - QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, const QUICPacket &initial_packet, QUICPacketFactory *packet_factory, - const QUICPreferredAddress *pref_addr); + QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, const QUICInitialPacketR &initial_packet, + QUICPacketFactory *packet_factory, const QUICPreferredAddress *pref_addr); QUICConnectionErrorUPtr do_handshake(); @@ -77,6 +78,7 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator bool is_version_negotiated() const; bool is_completed() const; + bool is_confirmed() const; bool is_stateless_retry_enabled() const; bool has_remote_tp() const; @@ -102,4 +104,10 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator std::shared_ptr _remote_transport_parameters = nullptr; void _abort_handshake(QUICTransErrorCode code); + + bool _is_handshake_done_sent = false; + bool _is_handshake_done_received = false; + + // QUICFrameGenerator + void _on_frame_lost(QUICFrameInformationUPtr &info) override; }; diff --git a/iocore/net/quic/QUICHandshakeProtocol.h b/iocore/net/quic/QUICHandshakeProtocol.h index fd4c68b65c1..2d56e020f09 100644 --- a/iocore/net/quic/QUICHandshakeProtocol.h +++ b/iocore/net/quic/QUICHandshakeProtocol.h @@ -43,7 +43,7 @@ class QUICHandshakeProtocol QUICHandshakeProtocol(QUICPacketProtectionKeyInfo &pp_key_info) : _pp_key_info(pp_key_info) {} virtual ~QUICHandshakeProtocol(){}; - virtual int handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) = 0; + virtual int handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) = 0; virtual void reset() = 0; virtual bool is_handshake_finished() const = 0; virtual bool is_ready_to_derive() const = 0; @@ -58,6 +58,8 @@ class QUICHandshakeProtocol virtual QUICEncryptionLevel current_encryption_level() const = 0; virtual void abort_handshake() = 0; + virtual bool has_crypto_error() const = 0; + virtual uint64_t crypto_error() const = 0; protected: QUICPacketProtectionKeyInfo &_pp_key_info; diff --git a/iocore/net/quic/QUICIntUtil.cc b/iocore/net/quic/QUICIntUtil.cc index e9840ca2d61..26159d74770 100644 --- a/iocore/net/quic/QUICIntUtil.cc +++ b/iocore/net/quic/QUICIntUtil.cc @@ -102,11 +102,11 @@ QUICVariableInt::decode(uint64_t &dst, size_t &len, const uint8_t *src, size_t s } uint64_t -QUICIntUtil::read_QUICVariableInt(const uint8_t *buf) +QUICIntUtil::read_QUICVariableInt(const uint8_t *buf, size_t buf_len) { uint64_t dst = 0; size_t len = 0; - QUICVariableInt::decode(dst, len, buf, 8); + QUICVariableInt::decode(dst, len, buf, buf_len); return dst; } diff --git a/iocore/net/quic/QUICIntUtil.h b/iocore/net/quic/QUICIntUtil.h index c259bca682a..5116304643a 100644 --- a/iocore/net/quic/QUICIntUtil.h +++ b/iocore/net/quic/QUICIntUtil.h @@ -38,7 +38,7 @@ class QUICVariableInt class QUICIntUtil { public: - static uint64_t read_QUICVariableInt(const uint8_t *buf); + static uint64_t read_QUICVariableInt(const uint8_t *buf, size_t buf_len); static void write_QUICVariableInt(uint64_t data, uint8_t *buf, size_t *len); static uint64_t read_nbytes_as_uint(const uint8_t *buf, uint8_t n); static void write_uint_as_nbytes(uint64_t value, uint8_t n, uint8_t *buf, size_t *len); diff --git a/iocore/net/quic/QUICKeyGenerator.cc b/iocore/net/quic/QUICKeyGenerator.cc index 98d091d8725..24c2b84628b 100644 --- a/iocore/net/quic/QUICKeyGenerator.cc +++ b/iocore/net/quic/QUICKeyGenerator.cc @@ -45,8 +45,8 @@ 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) { - const QUIC_EVP_CIPHER *cipher = this->_get_cipher_for_initial(); - const EVP_MD *md = EVP_sha256(); + const EVP_CIPHER *cipher = this->_get_cipher_for_initial(); + const EVP_MD *md = EVP_sha256(); uint8_t secret[512]; size_t secret_len = sizeof(secret); QUICHKDF hkdf(md); @@ -79,14 +79,14 @@ QUICKeyGenerator::generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t void QUICKeyGenerator::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 QUIC_EVP_CIPHER *cipher, QUICHKDF &hkdf) + size_t secret_len, const EVP_CIPHER *cipher, QUICHKDF &hkdf) { this->_generate(hp_key, pp_key, iv, iv_len, hkdf, secret, secret_len, cipher); } int QUICKeyGenerator::_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 QUIC_EVP_CIPHER *cipher) + size_t secret_len, const EVP_CIPHER *cipher) { // Generate key, iv, and hp_key // key = HKDF-Expand-Label(S, "quic key", "", key_length) diff --git a/iocore/net/quic/QUICKeyGenerator.h b/iocore/net/quic/QUICKeyGenerator.h index a82ad43a2a3..c2ff89f78fa 100644 --- a/iocore/net/quic/QUICKeyGenerator.h +++ b/iocore/net/quic/QUICKeyGenerator.h @@ -46,7 +46,7 @@ class QUICKeyGenerator void generate(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 QUIC_EVP_CIPHER *cipher, QUICHKDF &hkdf); + const EVP_CIPHER *cipher, QUICHKDF &hkdf); private: Context _ctx = Context::SERVER; @@ -55,15 +55,15 @@ class QUICKeyGenerator size_t _last_secret_len = 0; 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 QUIC_EVP_CIPHER *cipher); + 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_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; int _generate_hp(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t hp_length) const; - size_t _get_key_len(const QUIC_EVP_CIPHER *cipher) const; - size_t _get_iv_len(const QUIC_EVP_CIPHER *cipher) const; - const QUIC_EVP_CIPHER *_get_cipher_for_initial() const; - const QUIC_EVP_CIPHER *_get_cipher_for_protected_packet(const SSL *ssl) const; + size_t _get_key_len(const EVP_CIPHER *cipher) const; + size_t _get_iv_len(const EVP_CIPHER *cipher) const; + const EVP_CIPHER *_get_cipher_for_initial() const; + const EVP_CIPHER *_get_cipher_for_protected_packet(const SSL *ssl) const; }; diff --git a/iocore/net/quic/QUICKeyGenerator_boringssl.cc b/iocore/net/quic/QUICKeyGenerator_boringssl.cc index e2204bbd3b0..4406bbd376c 100644 --- a/iocore/net/quic/QUICKeyGenerator_boringssl.cc +++ b/iocore/net/quic/QUICKeyGenerator_boringssl.cc @@ -25,33 +25,34 @@ #include size_t -QUICKeyGenerator::_get_key_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_key_len(const EVP_CIPHER *cipher) const { - return EVP_AEAD_key_length(cipher); + return EVP_CIPHER_key_length(cipher); } size_t -QUICKeyGenerator::_get_iv_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_iv_len(const EVP_CIPHER *cipher) const { - return EVP_AEAD_nonce_length(cipher); + return EVP_CIPHER_iv_length(cipher); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_initial() const { - return EVP_aead_aes_128_gcm(); + return EVP_aes_128_gcm(); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { case TLS1_CK_AES_128_GCM_SHA256: - return EVP_aead_aes_128_gcm(); + return EVP_aes_128_gcm(); case TLS1_CK_AES_256_GCM_SHA384: - return EVP_aead_aes_256_gcm(); + return EVP_aes_256_gcm(); case TLS1_CK_CHACHA20_POLY1305_SHA256: - return EVP_aead_chacha20_poly1305(); + return nullptr; + // return EVP_aead_chacha20_poly1305(); default: ink_assert(false); return nullptr; diff --git a/iocore/net/quic/QUICKeyGenerator_legacy.cc b/iocore/net/quic/QUICKeyGenerator_legacy.cc new file mode 100644 index 00000000000..03b46e1cf1e --- /dev/null +++ b/iocore/net/quic/QUICKeyGenerator_legacy.cc @@ -0,0 +1,63 @@ +/** @file + * + * A key generator for QUIC connection (OpenSSL specific parts) + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "tscore/ink_assert.h" +#include "QUICKeyGenerator.h" + +#include + +size_t +QUICKeyGenerator::_get_key_len(const EVP_CIPHER *cipher) const +{ + return EVP_CIPHER_key_length(cipher); +} + +size_t +QUICKeyGenerator::_get_iv_len(const EVP_CIPHER *cipher) const +{ + return EVP_CIPHER_iv_length(cipher); +} + +const EVP_CIPHER * +QUICKeyGenerator::_get_cipher_for_initial() const +{ + return EVP_aes_128_gcm(); +} + +const EVP_CIPHER * +QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + return EVP_aes_128_gcm(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_aes_256_gcm(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return EVP_chacha20_poly1305(); + case TLS1_3_CK_AES_128_CCM_SHA256: + case TLS1_3_CK_AES_128_CCM_8_SHA256: + return EVP_aes_128_ccm(); + default: + ink_assert(false); + return nullptr; + } +} diff --git a/iocore/net/quic/QUICKeyGenerator_openssl.cc b/iocore/net/quic/QUICKeyGenerator_openssl.cc index 5d9d0294e0f..e7bb227faf0 100644 --- a/iocore/net/quic/QUICKeyGenerator_openssl.cc +++ b/iocore/net/quic/QUICKeyGenerator_openssl.cc @@ -24,26 +24,25 @@ #include "QUICKeyGenerator.h" #include - size_t -QUICKeyGenerator::_get_key_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_key_len(const EVP_CIPHER *cipher) const { return EVP_CIPHER_key_length(cipher); } size_t -QUICKeyGenerator::_get_iv_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_iv_len(const EVP_CIPHER *cipher) const { return EVP_CIPHER_iv_length(cipher); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_initial() const { return EVP_aes_128_gcm(); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { @@ -61,3 +60,21 @@ QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const return nullptr; } } + +// SSL_HANDSHAKE_MAC_SHA256, SSL_HANDSHAKE_MAC_SHA384 are defind in `ssl/internal.h` of BoringSSL +/* +const EVP_MD * +QUICKeyGenerator::get_handshake_digest(const SSL *ssl) +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return EVP_sha256(); + case TLS1_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + ink_assert(false); + return nullptr; + } +} +*/ diff --git a/iocore/net/quic/QUICLossDetector.cc b/iocore/net/quic/QUICLossDetector.cc index 4d6397bbfab..6a0138d30a4 100644 --- a/iocore/net/quic/QUICLossDetector.cc +++ b/iocore/net/quic/QUICLossDetector.cc @@ -38,7 +38,7 @@ #define QUICLDVDebug(fmt, ...) \ Debug("v_quic_loss_detector", "[%s] " fmt, this->_context.connection_info()->cids().data(), ##__VA_ARGS__) -QUICLossDetector::QUICLossDetector(QUICLDContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, +QUICLossDetector::QUICLossDetector(QUICContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, QUICPinger *pinger, QUICPadder *padder) : _rtt_measure(rtt_measure), _pinger(pinger), _padder(padder), _cc(cc), _context(context) { @@ -444,6 +444,7 @@ QUICLossDetector::_detect_lost_packets(QUICPacketNumberSpace pn_space) 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. @@ -487,7 +488,7 @@ QUICLossDetector::_send_packet(QUICEncryptionLevel level, bool padded) if (padded) { this->_padder->request(level); } else { - this->_pinger->request(); + this->_pinger->request(level); } this->_cc->add_extra_credit(); } @@ -497,23 +498,25 @@ QUICLossDetector::_send_one_or_two_packet() { this->_send_packet(QUICEncryptionLevel::ONE_RTT); this->_send_packet(QUICEncryptionLevel::ONE_RTT); - ink_assert(this->_pinger->count() >= 2); + ink_assert(this->_pinger->count(QUICEncryptionLevel::ONE_RTT) >= 2); QUICLDDebug("[%s] send ping frame %" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::ONE_RTT), - this->_pinger->count()); + this->_pinger->count(QUICEncryptionLevel::ONE_RTT)); } void QUICLossDetector::_send_one_handshake_packets() { this->_send_packet(QUICEncryptionLevel::HANDSHAKE); - QUICLDDebug("[%s] send handshake packet", QUICDebugNames::encryption_level(QUICEncryptionLevel::HANDSHAKE)); + QUICLDDebug("[%s] send handshake packet: ping count=%" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::HANDSHAKE), + this->_pinger->count(QUICEncryptionLevel::HANDSHAKE)); } void QUICLossDetector::_send_one_padded_packets() { this->_send_packet(QUICEncryptionLevel::INITIAL, true); - QUICLDDebug("[%s] send PADDING frame", QUICDebugNames::encryption_level(QUICEncryptionLevel::INITIAL)); + QUICLDDebug("[%s] send PADDING frame: ping count=%" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::INITIAL), + this->_pinger->count(QUICEncryptionLevel::INITIAL)); } // ===== Functions below are helper functions ===== diff --git a/iocore/net/quic/QUICLossDetector.h b/iocore/net/quic/QUICLossDetector.h index 5e4005b8546..42afd81891f 100644 --- a/iocore/net/quic/QUICLossDetector.h +++ b/iocore/net/quic/QUICLossDetector.h @@ -59,7 +59,7 @@ class QUICRTTProvider class QUICNewRenoCongestionController : public QUICCongestionController { public: - QUICNewRenoCongestionController(QUICCCContext &context); + QUICNewRenoCongestionController(QUICContext &context); virtual ~QUICNewRenoCongestionController() {} void on_packet_sent(size_t bytes_sent) override; void on_packet_acked(const QUICPacketInfo &acked_packet) override; @@ -71,9 +71,9 @@ class QUICNewRenoCongestionController : public QUICCongestionController bool is_app_limited(); // Debug - uint32_t bytes_in_flight() const; - uint32_t congestion_window() const; - uint32_t current_ssthresh() const; + uint32_t bytes_in_flight() const override; + uint32_t congestion_window() const override; + uint32_t current_ssthresh() const override; void add_extra_credit() override; @@ -105,13 +105,13 @@ class QUICNewRenoCongestionController : public QUICCongestionController bool _in_congestion_recovery(ink_hrtime sent_time); - QUICCCContext &_context; + QUICContext &_context; }; class QUICLossDetector : public Continuation, public QUICFrameHandler { public: - QUICLossDetector(QUICLDContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, QUICPinger *pinger, + QUICLossDetector(QUICContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, QUICPinger *pinger, QUICPadder *padder); ~QUICLossDetector(); @@ -185,7 +185,7 @@ class QUICLossDetector : public Continuation, public QUICFrameHandler QUICPadder *_padder = nullptr; QUICCongestionController *_cc = nullptr; - QUICLDContext &_context; + QUICContext &_context; }; class QUICRTTMeasure : public QUICRTTProvider diff --git a/iocore/net/quic/QUICNewRenoCongestionController.cc b/iocore/net/quic/QUICNewRenoCongestionController.cc index 080c910ed4c..db6ec90a149 100644 --- a/iocore/net/quic/QUICNewRenoCongestionController.cc +++ b/iocore/net/quic/QUICNewRenoCongestionController.cc @@ -38,7 +38,7 @@ this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \ this->_extra_packets_count, ##__VA_ARGS__) -QUICNewRenoCongestionController::QUICNewRenoCongestionController(QUICCCContext &context) +QUICNewRenoCongestionController::QUICNewRenoCongestionController(QUICContext &context) : _cc_mutex(new_ProxyMutex()), _context(context) { auto &cc_config = context.cc_config(); @@ -94,10 +94,13 @@ QUICNewRenoCongestionController::on_packet_acked(const QUICPacketInfo &acked_pac 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 chaged"); } 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"); } @@ -116,6 +119,9 @@ QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time) this->_congestion_window *= this->_k_loss_reduction_factor; this->_congestion_window = std::max(this->_congestion_window, this->_k_minimum_window); this->_ssthresh = this->_congestion_window; + 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); } } diff --git a/iocore/net/quic/QUICPacket.cc b/iocore/net/quic/QUICPacket.cc index 534ed2770f7..494dd3249f8 100644 --- a/iocore/net/quic/QUICPacket.cc +++ b/iocore/net/quic/QUICPacket.cc @@ -23,258 +23,438 @@ #include "QUICPacket.h" +#include + #include #include #include "QUICIntUtil.h" #include "QUICDebugNames.h" +#include "QUICRetryIntegrityTag.h" using namespace std::literals; -static constexpr std::string_view tag = "quic_packet"sv; -static constexpr uint64_t aead_tag_len = 16; +static constexpr uint64_t aead_tag_len = 16; +static constexpr int LONG_HDR_OFFSET_CONNECTION_ID = 6; +static constexpr int LONG_HDR_OFFSET_VERSION = 1; #define QUICDebug(dcid, scid, fmt, ...) \ Debug(tag.data(), "[%08" PRIx32 "-%08" PRIx32 "] " fmt, dcid.h32(), scid.h32(), ##__VA_ARGS__); -ClassAllocator quicPacketAllocator("quicPacketAllocator"); -ClassAllocator quicPacketLongHeaderAllocator("quicPacketLongHeaderAllocator"); -ClassAllocator quicPacketShortHeaderAllocator("quicPacketShortHeaderAllocator"); - -static constexpr int LONG_HDR_OFFSET_CONNECTION_ID = 6; -static constexpr int LONG_HDR_OFFSET_VERSION = 1; - // -// QUICPacketHeader +// QUICPacket // -const uint8_t * -QUICPacketHeader::buf() -{ - if (this->_buf) { - return this->_buf.get(); - } else { - // TODO Reuse serialzied data if nothing has changed - this->store(this->_serialized, &this->_buf_len); - if (this->_buf_len > MAX_PACKET_HEADER_LEN) { - ink_assert(!"Serialized packet header is too long"); - } +QUICPacket::QUICPacket() {} - this->_buf_len += this->_payload_length; - return this->_serialized; - } -} +QUICPacket::QUICPacket(bool ack_eliciting, bool probing) : _is_ack_eliciting(ack_eliciting), _is_probing_packet(probing) {} -const IpEndpoint & -QUICPacketHeader::from() const +QUICPacket::~QUICPacket() {} + +QUICKeyPhase +QUICPacket::key_phase() const { - return this->_from; + ink_assert(!"This function should not be called"); + return QUICKeyPhase::INITIAL; } -const IpEndpoint & -QUICPacketHeader::to() const +bool +QUICPacket::is_ack_eliciting() const { - return this->_to; + return this->_is_ack_eliciting; } bool -QUICPacketHeader::is_crypto_packet() const +QUICPacket::is_probing_packet() const { - return false; + return this->_is_probing_packet; } uint16_t -QUICPacketHeader::packet_size() const +QUICPacket::header_size() const { - return this->_buf_len; -} + uint16_t size = 0; -QUICPacketHeaderUPtr -QUICPacketHeader::load(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base) -{ - QUICPacketHeaderUPtr header = QUICPacketHeaderUPtr(nullptr, &QUICPacketHeaderDeleter::delete_null_header); - if (QUICInvariants::is_long_header(buf.get())) { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(from, to, std::move(buf), len, base); - header = QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); - } else { - QUICPacketShortHeader *short_header = quicPacketShortHeaderAllocator.alloc(); - new (short_header) QUICPacketShortHeader(from, to, std::move(buf), len, base); - header = QUICPacketHeaderUPtr(short_header, &QUICPacketHeaderDeleter::delete_short_header); + for (auto b = this->header_block(); b; b = b->next) { + size += b->size(); } - return header; + + return size; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, - ats_unique_buf payload, size_t len) +uint16_t +QUICPacket::payload_length() const { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(type, key_phase, destination_cid, source_cid, packet_number, base_packet_number, version, - crypto, std::move(payload), len); - return QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); + uint16_t size = 0; + + for (auto b = this->payload_block(); b; b = b->next) { + size += b->size(); + } + + return size; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, - ats_unique_buf payload, size_t len, ats_unique_buf token, size_t token_len) +uint16_t +QUICPacket::size() const { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(type, key_phase, destination_cid, source_cid, packet_number, base_packet_number, version, - crypto, std::move(payload), len, std::move(token), token_len); - return QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); + return this->header_size() + this->payload_length(); } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, QUICConnectionId destination_cid, - QUICConnectionId source_cid, QUICConnectionId original_dcid, ats_unique_buf retry_token, - size_t retry_token_len) +void +QUICPacket::store(uint8_t *buf, size_t *len) const { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(type, key_phase, version, destination_cid, source_cid, original_dcid, - std::move(retry_token), retry_token_len); - return QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); + size_t written = 0; + Ptr block; + + block = this->header_block(); + while (block) { + memcpy(buf + written, block->start(), block->size()); + written += block->size(); + block = block->next; + } + + block = this->payload_block(); + while (block) { + memcpy(buf + written, block->start(), block->size()); + written += block->size(); + block = block->next; + } + + *len = written; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len) +uint8_t +QUICPacket::calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base) { - QUICPacketShortHeader *short_header = quicPacketShortHeaderAllocator.alloc(); - new (short_header) QUICPacketShortHeader(type, key_phase, packet_number, base_packet_number, std::move(payload), len); - return QUICPacketHeaderUPtr(short_header, &QUICPacketHeaderDeleter::delete_short_header); + uint64_t d = (num - base) * 2; + uint8_t len = 0; + + if (d > 0xFFFFFF) { + len = 4; + } else if (d > 0xFFFF) { + len = 3; + } else if (d > 0xFF) { + len = 2; + } else { + len = 1; + } + + return len; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId connection_id, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len) +bool +QUICPacket::encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len) { - QUICPacketShortHeader *short_header = quicPacketShortHeaderAllocator.alloc(); - new (short_header) - QUICPacketShortHeader(type, key_phase, connection_id, packet_number, base_packet_number, std::move(payload), len); - return QUICPacketHeaderUPtr(short_header, &QUICPacketHeaderDeleter::delete_short_header); + uint64_t mask = 0; + switch (len) { + case 1: + mask = 0xFF; + break; + case 2: + mask = 0xFFFF; + break; + case 3: + mask = 0xFFFFFF; + break; + case 4: + mask = 0xFFFFFFFF; + break; + default: + ink_assert(!"len must be 1, 2, or 4"); + return false; + } + dst = src & mask; + + return true; } -QUICPacketHeaderUPtr -QUICPacketHeader::clone() const +bool +QUICPacket::decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked) { - return QUICPacketHeaderUPtr(nullptr, &QUICPacketHeaderDeleter::delete_null_header); + ink_assert(len == 1 || len == 2 || len == 3 || len == 4); + + uint64_t maximum_diff = 0; + switch (len) { + case 1: + maximum_diff = 0x100; + break; + case 2: + maximum_diff = 0x10000; + break; + case 3: + maximum_diff = 0x1000000; + break; + case 4: + maximum_diff = 0x100000000; + break; + default: + ink_assert(!"len must be 1, 2, 3 or 4"); + } + QUICPacketNumber base = largest_acked & (~(maximum_diff - 1)); + QUICPacketNumber candidate1 = base + src; + QUICPacketNumber candidate2 = base + src + maximum_diff; + QUICPacketNumber expected = largest_acked + 1; + + if (((candidate1 > expected) ? (candidate1 - expected) : (expected - candidate1)) < + ((candidate2 > expected) ? (candidate2 - expected) : (expected - candidate2))) { + dst = candidate1; + } else { + dst = candidate2; + } + + return true; } // -// QUICPacketLongHeader +// QUICPacketR // -QUICPacketLongHeader::QUICPacketLongHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base) - : QUICPacketHeader(from, to, std::move(buf), len, base) +QUICPacketR::QUICPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to) : _udp_con(udp_con), _from(from), _to(to) {} + +UDPConnection * +QUICPacketR::udp_con() const { - this->_key_phase = QUICTypeUtil::key_phase(this->type()); - uint8_t *raw_buf = this->_buf.get(); + return this->_udp_con; +} - uint8_t dcil = 0; - uint8_t scil = 0; - QUICPacketLongHeader::dcil(dcil, raw_buf, len); - QUICPacketLongHeader::scil(scil, raw_buf, len); +const IpEndpoint & +QUICPacketR::from() const +{ + return this->_from; +} - size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; - this->_destination_cid = {raw_buf + offset, dcil}; - offset += dcil + 1; - this->_source_cid = {raw_buf + offset, scil}; - offset += scil; +const IpEndpoint & +QUICPacketR::to() const +{ + return this->_to; +} - if (this->type() != QUICPacketType::VERSION_NEGOTIATION) { - if (this->type() == QUICPacketType::RETRY) { - uint8_t odcil = raw_buf[offset]; - offset += 1; +bool +QUICPacketR::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) +{ + if (packet_len < 1) { + return false; + } - this->_original_dcid = {raw_buf + offset, odcil}; - offset += odcil; - } else { - if (this->type() == QUICPacketType::INITIAL) { - // Token Length Field - this->_token_len = QUICIntUtil::read_QUICVariableInt(raw_buf + offset); - offset += QUICVariableInt::size(raw_buf + offset); - // Token Field - this->_token_offset = offset; - offset += this->_token_len; - } + if (QUICInvariants::is_long_header(packet)) { + return QUICLongHeaderPacketR::type(type, packet, packet_len); + } else { + type = QUICPacketType::PROTECTED; + return true; + } +} + +bool +QUICPacketR::read_essential_info(Ptr block, QUICPacketType &type, QUICVersion &version, QUICConnectionId &dcid, + QUICConnectionId &scid, QUICPacketNumber &packet_number, QUICPacketNumber base_packet_number, + QUICKeyPhase &key_phase) +{ + uint8_t tmp[47 + 64]; + IOBufferReader reader; + reader.block = block; + int64_t len = std::min(static_cast(sizeof(tmp)), reader.read_avail()); - // Length Field - offset += QUICVariableInt::size(raw_buf + offset); + if (len < 10) { + return false; + } - // PN Field - int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); - QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, - this->_base_packet_number); - offset += pn_len; + reader.memcpy(tmp, 1, 0); + if (QUICInvariants::is_long_header(tmp)) { + reader.memcpy(tmp, len, 0); + type = static_cast((0x30 & tmp[0]) >> 4); + QUICInvariants::version(version, tmp, len); + if (version == 0x00) { + type = QUICPacketType::VERSION_NEGOTIATION; + } + if (!QUICInvariants::dcid(dcid, tmp, len) || !QUICInvariants::scid(scid, tmp, len)) { + return false; + } + if (type != QUICPacketType::RETRY) { + int packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(tmp); + size_t length_offset = 7 + dcid.length() + scid.length(); + if (length_offset >= static_cast(len)) { + return false; + } + uint64_t value; + size_t field_len; + QUICVariableInt::decode(value, field_len, tmp + length_offset); + switch (type) { + case QUICPacketType::INITIAL: + length_offset += field_len + value; + if (length_offset >= static_cast(len)) { + return false; + } + QUICVariableInt::decode(value, field_len, tmp + length_offset); + if (length_offset + field_len >= static_cast(len)) { + return false; + } + if (length_offset + field_len + packet_number_len > static_cast(len)) { + return false; + } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); + key_phase = QUICKeyPhase::INITIAL; + break; + case QUICPacketType::ZERO_RTT_PROTECTED: + if (length_offset + field_len + packet_number_len >= static_cast(len)) { + return false; + } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); + key_phase = QUICKeyPhase::ZERO_RTT; + break; + case QUICPacketType::HANDSHAKE: + if (length_offset + field_len + packet_number_len >= static_cast(len)) { + return false; + } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); + key_phase = QUICKeyPhase::INITIAL; + break; + case QUICPacketType::VERSION_NEGOTIATION: + break; + default: + break; + } + } else { + packet_number = 0; + } + } else { + len = std::min(static_cast(25), len); + reader.memcpy(tmp, len, 0); + type = QUICPacketType::PROTECTED; + QUICInvariants::dcid(dcid, tmp, len); + int packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(tmp); + if (tmp[0] & 0x04) { + key_phase = QUICKeyPhase::PHASE_1; + } else { + key_phase = QUICKeyPhase::PHASE_0; } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + 1 + dcid.length(), packet_number_len); } + return true; +} - this->_payload_offset = offset; - this->_payload_length = len - this->_payload_offset; +// +// QUICLongHeaderPacket +// +QUICLongHeaderPacket::QUICLongHeaderPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, bool ack_eliciting, + bool probing, bool crypto) + : QUICPacket(ack_eliciting, probing), _version(version), _dcid(dcid), _scid(scid), _is_crypto_packet(crypto) +{ } -QUICPacketLongHeader::QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &destination_cid, - const QUICConnectionId &source_cid, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, - ats_unique_buf buf, size_t len, ats_unique_buf token, size_t token_len) - : QUICPacketHeader(type, packet_number, base_packet_number, true, version, std::move(buf), len, key_phase), - _destination_cid(destination_cid), - _source_cid(source_cid), - _token_len(token_len), - _token(std::move(token)), - _is_crypto_packet(crypto) +QUICConnectionId +QUICLongHeaderPacket::destination_cid() const { - if (this->_type == QUICPacketType::VERSION_NEGOTIATION) { - this->_buf_len = - LONG_HDR_OFFSET_CONNECTION_ID + this->_destination_cid.length() + 1 + this->_source_cid.length() + this->_payload_length; - } else { - this->buf(); - } + return this->_dcid; } -QUICPacketLongHeader::QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, - const QUICConnectionId &destination_cid, const QUICConnectionId &source_cid, - const QUICConnectionId &original_dcid, ats_unique_buf retry_token, - size_t retry_token_len) - : QUICPacketHeader(type, 0, 0, true, version, std::move(retry_token), retry_token_len, key_phase), - _destination_cid(destination_cid), - _source_cid(source_cid), - _original_dcid(original_dcid) +QUICConnectionId +QUICLongHeaderPacket::source_cid() const +{ + return this->_scid; +} +uint16_t +QUICLongHeaderPacket::payload_length() const { - // this->_buf_len will be set - this->buf(); + return this->_payload_length; } -QUICPacketType -QUICPacketLongHeader::type() const +QUICVersion +QUICLongHeaderPacket::version() const +{ + return this->_version; +} + +size_t +QUICLongHeaderPacket::_write_common_header(uint8_t *buf) const { - if (this->_buf) { - QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, this->_buf.get(), this->_buf_len); - return type; + size_t n; + size_t len = 0; + + buf[0] = 0xC0; + buf[0] += static_cast(this->type()) << 4; + len += 1; + + QUICTypeUtil::write_QUICVersion(this->_version, buf + len, &n); + len += n; + + // DICD + if (this->_dcid != QUICConnectionId::ZERO()) { + // Len + buf[len] = this->_dcid.length(); + len += 1; + + // ID + QUICTypeUtil::write_QUICConnectionId(this->_dcid, buf + len, &n); + len += n; + } else { + buf[len] = 0; + len += 1; + } + + // SCID + if (this->_scid != QUICConnectionId::ZERO()) { + // Len + buf[len] = this->_scid.length(); + len += 1; + + // ID + QUICTypeUtil::write_QUICConnectionId(this->_scid, buf + len, &n); + len += n; } else { - return this->_type; + buf[len] = 0; + len += 1; } + + return len; } bool -QUICPacketLongHeader::is_crypto_packet() const +QUICLongHeaderPacket::is_crypto_packet() const { return this->_is_crypto_packet; } +// +// QUICLongHeaderPacketR +// +QUICLongHeaderPacketR::QUICLongHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks) + : QUICPacketR(udp_con, from, to) +{ + IOBufferReader reader; + uint8_t data[47]; + + reader.block = blocks; + int64_t data_len = reader.read(data, sizeof(data)); + + QUICLongHeaderPacketR::version(this->_version, data, data_len); +} + +QUICVersion +QUICLongHeaderPacketR::version() const +{ + return this->_version; +} + +QUICConnectionId +QUICLongHeaderPacketR::source_cid() const +{ + return this->_scid; +} + +QUICConnectionId +QUICLongHeaderPacketR::destination_cid() const +{ + return this->_dcid; +} + bool -QUICPacketLongHeader::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) { if (packet_len < 1) { return false; } QUICVersion version; - if (QUICPacketLongHeader::version(version, packet, packet_len) && version == 0x00) { + if (QUICLongHeaderPacketR::version(version, packet, packet_len) && version == 0x00) { type = QUICPacketType::VERSION_NEGOTIATION; } else { uint8_t raw_type = (packet[0] & 0x30) >> 4; @@ -284,7 +464,7 @@ QUICPacketLongHeader::type(QUICPacketType &type, const uint8_t *packet, size_t p } bool -QUICPacketLongHeader::version(QUICVersion &version, const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::version(QUICVersion &version, const uint8_t *packet, size_t packet_len) { if (packet_len < 5) { return false; @@ -295,101 +475,70 @@ QUICPacketLongHeader::version(QUICVersion &version, const uint8_t *packet, size_ } bool -QUICPacketLongHeader::dcil(uint8_t &dcil, const uint8_t *packet, size_t packet_len) -{ - if (QUICInvariants::dcil(dcil, packet, packet_len)) { - return true; - } else { - return false; - } -} - -bool -QUICPacketLongHeader::scil(uint8_t &scil, const uint8_t *packet, size_t packet_len) -{ - if (QUICInvariants::scil(scil, packet, packet_len)) { - return true; - } else { - return false; - } -} - -bool -QUICPacketLongHeader::token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, - const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::key_phase(QUICKeyPhase &phase, const uint8_t *packet, size_t packet_len) { QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, packet, packet_len); - - if (type != QUICPacketType::INITIAL) { - token_length = 0; - field_len = 0; - - return true; - } - - uint8_t dcil, scil; - QUICPacketLongHeader::dcil(dcil, packet, packet_len); - QUICPacketLongHeader::scil(scil, packet, packet_len); - - token_length_filed_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; - if (token_length_filed_offset >= packet_len) { - return false; - } - - token_length = QUICIntUtil::read_QUICVariableInt(packet + token_length_filed_offset); - field_len = QUICVariableInt::size(packet + token_length_filed_offset); - + QUICLongHeaderPacketR::type(type, packet, packet_len); + phase = QUICTypeUtil::key_phase(type); return true; } bool -QUICPacketLongHeader::length(size_t &length, uint8_t &length_field_len, size_t &length_field_offset, const uint8_t *packet, - size_t packet_len) +QUICLongHeaderPacketR::length(size_t &length, uint8_t &length_field_len, size_t &length_field_offset, const uint8_t *packet, + size_t packet_len) { + // FIXME This is not great because each packet types have different formats. + // We should remove this function and have length() on each packet type classes instead. + uint8_t dcil; - if (!QUICPacketLongHeader::dcil(dcil, packet, packet_len)) { + if (!QUICInvariants::dcil(dcil, packet, packet_len)) { return false; } uint8_t scil; - if (!QUICPacketLongHeader::scil(scil, packet, packet_len)) { + if (!QUICInvariants::scil(scil, packet, packet_len)) { return false; } - // Token Length (i) + Token (*) (for INITIAL packet) - size_t token_length = 0; - uint8_t token_length_field_len = 0; - size_t token_length_field_offset = 0; - if (!QUICPacketLongHeader::token_length(token_length, token_length_field_len, token_length_field_offset, packet, packet_len)) { - return false; + length_field_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; + + QUICPacketType type = QUICPacketType::UNINITIALIZED; + QUICLongHeaderPacketR::type(type, packet, packet_len); + if (type == QUICPacketType::INITIAL) { + // Token Length (i) + Token (*) (for INITIAL packet) + size_t token_length = 0; + uint8_t token_length_field_len = 0; + size_t token_length_field_offset = 0; + if (!QUICInitialPacketR::token_length(token_length, token_length_field_len, token_length_field_offset, packet, packet_len)) { + return false; + } + length_field_offset += token_length_field_len + token_length; } // Length (i) - length_field_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil + token_length_field_len + token_length; if (length_field_offset >= packet_len) { return false; } length_field_len = QUICVariableInt::size(packet + length_field_offset); - length = QUICIntUtil::read_QUICVariableInt(packet + length_field_offset); + length = QUICIntUtil::read_QUICVariableInt(packet + length_field_offset, packet_len - length_field_offset); return true; } bool -QUICPacketLongHeader::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::packet_length(size_t &packet_len, const uint8_t *buf, size_t buf_len) { size_t length; uint8_t length_field_len; size_t length_field_offset; - if (!QUICPacketLongHeader::length(length, length_field_len, length_field_offset, packet, packet_len)) { + if (!QUICLongHeaderPacketR::length(length, length_field_len, length_field_offset, buf, buf_len)) { return false; } - pn_offset = length_field_offset + length_field_len; + packet_len = length + length_field_offset + length_field_len; - if (pn_offset >= packet_len) { + if (packet_len > buf_len) { return false; } @@ -397,639 +546,1297 @@ QUICPacketLongHeader::packet_number_offset(size_t &pn_offset, const uint8_t *pac } bool -QUICPacketLongHeader::packet_length(size_t &packet_len, const uint8_t *buf, size_t buf_len) +QUICLongHeaderPacketR::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len) { - size_t length; + size_t dummy; uint8_t length_field_len; size_t length_field_offset; - if (!QUICPacketLongHeader::length(length, length_field_len, length_field_offset, buf, buf_len)) { + if (!QUICLongHeaderPacketR::length(dummy, length_field_len, length_field_offset, packet, packet_len)) { return false; } - packet_len = length + length_field_offset + length_field_len; + pn_offset = length_field_offset + length_field_len; - if (packet_len > buf_len) { + if (pn_offset >= packet_len) { return false; } return true; } -bool -QUICPacketLongHeader::key_phase(QUICKeyPhase &phase, const uint8_t *packet, size_t packet_len) +// +// QUICShortHeaderPacket +// +QUICShortHeaderPacket::QUICShortHeaderPacket(QUICConnectionId dcid, QUICPacketNumber packet_number, + QUICPacketNumber base_packet_number, QUICKeyPhase key_phase, bool ack_eliciting, + bool probing) + : QUICPacket(ack_eliciting, probing), _dcid(dcid), _packet_number(packet_number), _key_phase(key_phase) { - QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, packet, packet_len); - phase = QUICTypeUtil::key_phase(type); - return true; + this->_packet_number_len = QUICPacket::calc_packet_number_len(packet_number, base_packet_number); } -QUICConnectionId -QUICPacketLongHeader::destination_cid() const +QUICPacketType +QUICShortHeaderPacket::type() const { - return this->_destination_cid; + return QUICPacketType::PROTECTED; } -QUICConnectionId -QUICPacketLongHeader::source_cid() const +QUICKeyPhase +QUICShortHeaderPacket::key_phase() const { - return this->_source_cid; + return this->_key_phase; } QUICConnectionId -QUICPacketLongHeader::original_dcid() const +QUICShortHeaderPacket::destination_cid() const { - return this->_original_dcid; + return this->_dcid; } QUICPacketNumber -QUICPacketLongHeader::packet_number() const +QUICShortHeaderPacket::packet_number() const { return this->_packet_number; } -bool -QUICPacketLongHeader::has_version() const +uint16_t +QUICShortHeaderPacket::payload_length() const { - return true; + return this->_payload_length; } -bool -QUICPacketLongHeader::is_valid() const +Ptr +QUICShortHeaderPacket::header_block() const { - if (this->_buf && this->_buf_len != this->_payload_offset + this->_payload_length) { - QUICDebug(this->_source_cid, this->_destination_cid, - "Invalid packet: packet_size(%zu) should be header_size(%zu) + payload_size(%zu)", this->_buf_len, - this->_payload_offset, this->_payload_length); - Warning("Invalid packet: packet_size(%zu) should be header_size(%zu) + payload_size(%zu)", this->_buf_len, - this->_payload_offset, this->_payload_length); + Ptr block; + size_t written_len = 0; - return false; - } + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(1 + QUICConnectionId::MAX_LENGTH + 4, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); - return true; -} + size_t n; + buf[0] = 0x40; -QUICVersion -QUICPacketLongHeader::version() const -{ - if (this->_buf) { - QUICVersion version = 0; - QUICPacketLongHeader::version(version, this->_buf.get(), this->_buf_len); - return version; - } else { - return this->_version; - } -} + // Type + buf[0] = 0x40; -const uint8_t * -QUICPacketLongHeader::payload() const -{ - if (this->_buf) { - uint8_t *raw = this->_buf.get(); - return raw + this->_payload_offset; - } else { - return this->_payload.get(); + // TODO Spin Bit + + // KeyPhase + if (this->_key_phase == QUICKeyPhase::PHASE_1) { + buf[0] |= 0x04; } -} -uint16_t -QUICPacketHeader::payload_size() const -{ - return this->_payload_length; -} + written_len += 1; -const uint8_t * -QUICPacketLongHeader::token() const -{ - if (this->_buf) { - uint8_t *raw = this->_buf.get(); - return raw + this->_token_offset; - } else { - return this->_token.get(); + // Destination Connection ID + if (this->_dcid != QUICConnectionId::ZERO()) { + QUICTypeUtil::write_QUICConnectionId(this->_dcid, buf + written_len, &n); + written_len += n; } -} -size_t -QUICPacketLongHeader::token_len() const -{ - return this->_token_len; -} + // Packet Number + QUICPacketNumber dst = 0; + size_t dst_len = this->_packet_number_len; + QUICPacket::encode_packet_number(dst, this->_packet_number, dst_len); + QUICTypeUtil::write_QUICPacketNumber(dst, dst_len, buf + written_len, &n); + written_len += n; -QUICKeyPhase -QUICPacketLongHeader::key_phase() const -{ - return this->_key_phase; + // Packet Number Length + QUICTypeUtil::write_QUICPacketNumberLen(n, buf); + + block->fill(written_len); + + return block; } -uint16_t -QUICPacketLongHeader::size() const +Ptr +QUICShortHeaderPacket::payload_block() const { - return this->_buf_len - this->_payload_length; + return this->_payload_block; } void -QUICPacketLongHeader::store(uint8_t *buf, size_t *len) const +QUICShortHeaderPacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICShortHeaderPacketR +// +QUICShortHeaderPacketR::QUICShortHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICPacketR(udp_con, from, to) { - size_t n; - *len = 0; - buf[0] = 0xC0; - buf[0] += static_cast(this->_type) << 4; - if (this->_type == QUICPacketType::VERSION_NEGOTIATION) { - buf[0] |= rand(); + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); } - *len += 1; - QUICTypeUtil::write_QUICVersion(this->_version, buf + *len, &n); - *len += n; + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); - // DICD - if (this->_destination_cid != QUICConnectionId::ZERO()) { - // Len - buf[*len] = this->_destination_cid.length(); - *len += 1; + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); - // ID - QUICTypeUtil::write_QUICConnectionId(this->_destination_cid, buf + *len, &n); - *len += n; - } else { - buf[*len] = 0; - *len += 1; + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); } - // SCID - if (this->_source_cid != QUICConnectionId::ZERO()) { - // Len - buf[*len] = this->_source_cid.length(); - *len += 1; - - // ID - QUICTypeUtil::write_QUICConnectionId(this->_source_cid, buf + *len, &n); - *len += n; + if (raw_buf[0] & 0x04) { + this->_key_phase = QUICKeyPhase::PHASE_1; } else { - buf[*len] = 0; - *len += 1; + this->_key_phase = QUICKeyPhase::PHASE_0; } - if (this->_type != QUICPacketType::VERSION_NEGOTIATION) { - if (this->_type == QUICPacketType::RETRY) { - // Original Destination Connection ID - if (this->_original_dcid != QUICConnectionId::ZERO()) { - // Len - buf[*len] = this->_original_dcid.length(); - *len += 1; - - // ID - QUICTypeUtil::write_QUICConnectionId(this->_original_dcid, buf + *len, &n); - *len += n; - } else { - buf[*len] = 0; - *len += 1; - } - } else { - if (this->_type == QUICPacketType::INITIAL) { - // Token Length Field - QUICIntUtil::write_QUICVariableInt(this->_token_len, buf + *len, &n); - *len += n; - - // Token Field - memcpy(buf + *len, this->token(), this->token_len()); - *len += this->token_len(); - } - - QUICPacketNumber pn = 0; - size_t pn_len = 4; - QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); - - if (pn > 0x7FFFFF) { - pn_len = 4; - } else if (pn > 0x7FFF) { - pn_len = 3; - } else if (pn > 0x7F) { - pn_len = 2; - } else { - pn_len = 1; - } - - if (this->_type != QUICPacketType::RETRY) { - // PN Len field - QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); - } - - // Length Field - QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length + aead_tag_len, buf + *len, &n); - *len += n; + QUICInvariants::dcid(this->_dcid, raw_buf, len); - // PN Field - QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + *len, &n); - *len += n; - } + int offset = 1 + this->_dcid.length(); + this->_packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacketNumber src = QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, this->_packet_number_len); + QUICPacket::decode_packet_number(this->_packet_number, src, this->_packet_number_len, base_packet_number); + offset += this->_packet_number_len; - // Payload will be stored - } + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; } -// -// QUICPacketShortHeader -// - -QUICPacketShortHeader::QUICPacketShortHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base) - : QUICPacketHeader(from, to, std::move(buf), len, base) +QUICPacketType +QUICShortHeaderPacketR::type() const { - QUICInvariants::dcid(this->_connection_id, this->_buf.get(), len); - - int offset = 1 + this->_connection_id.length(); - this->_packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(this->_buf.get()); - QUICPacketNumber src = QUICTypeUtil::read_QUICPacketNumber(this->_buf.get() + offset, this->_packet_number_len); - QUICPacket::decode_packet_number(this->_packet_number, src, this->_packet_number_len, this->_base_packet_number); - this->_payload_length = len - (1 + QUICConnectionId::SCID_LEN + this->_packet_number_len); + return QUICPacketType::PROTECTED; } -QUICPacketShortHeader::QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf buf, size_t len) +QUICKeyPhase +QUICShortHeaderPacketR::key_phase() const { - this->_type = type; - this->_key_phase = key_phase; - this->_packet_number = packet_number; - this->_base_packet_number = base_packet_number; - this->_packet_number_len = QUICPacket::calc_packet_number_len(packet_number, base_packet_number); - this->_payload = std::move(buf); - this->_payload_length = len; + return this->_key_phase; } -QUICPacketShortHeader::QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &connection_id, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, - ats_unique_buf buf, size_t len) +QUICPacketNumber +QUICShortHeaderPacketR::packet_number() const { - this->_type = type; - this->_key_phase = key_phase; - this->_connection_id = connection_id; - this->_packet_number = packet_number; - this->_base_packet_number = base_packet_number; - this->_packet_number_len = QUICPacket::calc_packet_number_len(packet_number, base_packet_number); - this->_payload = std::move(buf); - this->_payload_length = len; + return this->_packet_number; } -QUICPacketType -QUICPacketShortHeader::type() const +QUICConnectionId +QUICShortHeaderPacketR::destination_cid() const { - QUICKeyPhase key_phase = this->key_phase(); - - switch (key_phase) { - case QUICKeyPhase::PHASE_0: { - return QUICPacketType::PROTECTED; - } - case QUICKeyPhase::PHASE_1: { - return QUICPacketType::PROTECTED; - } - default: - return QUICPacketType::STATELESS_RESET; - } + return this->_dcid; } -QUICConnectionId -QUICPacketShortHeader::destination_cid() const +Ptr +QUICShortHeaderPacketR::header_block() const { - if (this->_buf) { - QUICConnectionId dcid = QUICConnectionId::ZERO(); - QUICInvariants::dcid(dcid, this->_buf.get(), this->_buf_len); - return dcid; - } else { - return _connection_id; - } + return this->_header_block; } -QUICPacketNumber -QUICPacketShortHeader::packet_number() const +Ptr +QUICShortHeaderPacketR::payload_block() const { - return this->_packet_number; + return this->_payload_block; } -bool -QUICPacketShortHeader::has_version() const +void +QUICShortHeaderPacketR::attach_payload(Ptr payload, bool unprotected) { - return false; + this->_payload_block = payload; } bool -QUICPacketShortHeader::is_valid() const +QUICShortHeaderPacketR::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len, int dcil) { + pn_offset = 1 + dcil; return true; } -QUICVersion -QUICPacketShortHeader::version() const +// +// QUICStatelessResetPacket +// +QUICStatelessResetPacket::QUICStatelessResetPacket(QUICStatelessResetToken token, size_t maximum_size) + : QUICPacket(), _token(token), _maximum_size(maximum_size) { - return 0; } -const uint8_t * -QUICPacketShortHeader::payload() const +QUICPacketType +QUICStatelessResetPacket::type() const { - if (this->_buf) { - return this->_buf.get() + this->size(); - } else { - return this->_payload.get(); - } + return QUICPacketType::STATELESS_RESET; } -QUICKeyPhase -QUICPacketShortHeader::key_phase() const +QUICConnectionId +QUICStatelessResetPacket::destination_cid() const { - if (this->_buf) { - QUICKeyPhase phase = QUICKeyPhase::INITIAL; - QUICPacketShortHeader::key_phase(phase, this->_buf.get(), this->_buf_len); - return phase; - } else { - return this->_key_phase; - } + ink_assert(!"You should not need DCID of Stateless Reset Packet"); + return QUICConnectionId::ZERO(); } -bool -QUICPacketShortHeader::key_phase(QUICKeyPhase &phase, const uint8_t *packet, size_t packet_len) +Ptr +QUICStatelessResetPacket::header_block() const { - if (packet_len < 1) { - return false; + // Required shortest length is 38 bits however less than 41 bytes in total indicates this is stateless reset. + constexpr uint8_t MIN_UNPREDICTABLE_FIELD_LEN = 5 + 20; + + std::random_device rnd; + + Ptr block; + size_t written_len = 0; + + size_t random_extra_length = rnd() & 0x07; // Extra 0 to 7 bytes + + if (MIN_UNPREDICTABLE_FIELD_LEN + random_extra_length > this->_maximum_size) { + return block; } - if (packet[0] & 0x04) { - phase = QUICKeyPhase::PHASE_1; - } else { - phase = QUICKeyPhase::PHASE_0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(MIN_UNPREDICTABLE_FIELD_LEN + random_extra_length, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Generate random octets + for (int i = 0; i < MIN_UNPREDICTABLE_FIELD_LEN; ++i) { + buf[i] = static_cast(rnd() & 0xFF); } - return true; + buf[0] = (buf[0] | 0x40) & 0x7f; + written_len += MIN_UNPREDICTABLE_FIELD_LEN; + + block->fill(written_len); + + return block; } -bool -QUICPacketShortHeader::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len, int dcil) +Ptr +QUICStatelessResetPacket::payload_block() const { - pn_offset = 1 + dcil; - return true; + Ptr block; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(QUICStatelessResetToken::LEN, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + memcpy(buf, this->_token.buf(), QUICStatelessResetToken::LEN); + written_len += QUICStatelessResetToken::LEN; + + block->fill(written_len); + + return block; } -/** - * Header Length (doesn't include payload length) - */ -uint16_t -QUICPacketShortHeader::size() const +QUICPacketNumber +QUICStatelessResetPacket::packet_number() const { - uint16_t len = 1; - if (this->_connection_id != QUICConnectionId::ZERO()) { - len += this->_connection_id.length(); - } - len += this->_packet_number_len; + ink_assert(!"You should not need packet number of Stateless Reset Packet"); + return 0; +} - return len; +QUICStatelessResetToken +QUICStatelessResetPacket::token() const +{ + return this->_token; } -void -QUICPacketShortHeader::store(uint8_t *buf, size_t *len) const +// +// QUICStatelessResetPacketR +// +QUICStatelessResetPacketR::QUICStatelessResetPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, + Ptr blocks) + : QUICPacketR(udp_con, from, to) { - size_t n; - *len = 0; - buf[0] = 0x40; - if (this->_key_phase == QUICKeyPhase::PHASE_1) { - buf[0] |= 0x04; - } - *len += 1; +} - if (this->_connection_id != QUICConnectionId::ZERO()) { - QUICTypeUtil::write_QUICConnectionId(this->_connection_id, buf + *len, &n); - *len += n; - } +QUICPacketType +QUICStatelessResetPacketR::type() const +{ + return QUICPacketType::STATELESS_RESET; +} - QUICPacketNumber dst = 0; - size_t dst_len = this->_packet_number_len; - QUICPacket::encode_packet_number(dst, this->_packet_number, dst_len); - QUICTypeUtil::write_QUICPacketNumber(dst, dst_len, buf + *len, &n); - *len += n; +QUICPacketNumber +QUICStatelessResetPacketR::packet_number() const +{ + ink_assert(!"You should not need packet number of Stateless Reset Packet"); + return 0; +} - QUICTypeUtil::write_QUICPacketNumberLen(n, buf); +QUICConnectionId +QUICStatelessResetPacketR::destination_cid() const +{ + ink_assert(!"You should not need DCID of Stateless Reset Packet"); + return QUICConnectionId::ZERO(); } // -// QUICPacket +// QUICVersionNegotiationPacket // - -QUICPacket::QUICPacket() {} - -QUICPacket::QUICPacket(UDPConnection *udp_con, QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len) - : _udp_con(udp_con), _header(std::move(header)), _payload(std::move(payload)), _payload_size(payload_len) +QUICVersionNegotiationPacket::QUICVersionNegotiationPacket(QUICConnectionId dcid, QUICConnectionId scid, + const QUICVersion versions[], int nversions) + : QUICLongHeaderPacket(0, dcid, scid, false, false, false), _versions(versions), _nversions(nversions) { } -QUICPacket::QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, bool ack_eliciting, bool probing) - : _header(std::move(header)), - _payload(std::move(payload)), - _payload_size(payload_len), - _is_ack_eliciting(ack_eliciting), - _is_probing_packet(probing) +QUICPacketType +QUICVersionNegotiationPacket::type() const { + return QUICPacketType::VERSION_NEGOTIATION; } -QUICPacket::~QUICPacket() +QUICVersion +QUICVersionNegotiationPacket::version() const { - this->_header = nullptr; + return 0; } -const IpEndpoint & -QUICPacket::from() const +QUICPacketNumber +QUICVersionNegotiationPacket::packet_number() const { - return this->_header->from(); + ink_assert(!"You should not need packet number of Version Negotiation Packet"); + return 0; } -const IpEndpoint & -QUICPacket::to() const +uint16_t +QUICVersionNegotiationPacket::payload_length() const { - return this->_header->to(); + uint16_t size = 0; + + for (auto b = this->payload_block(); b; b = b->next) { + size += b->size(); + } + + return size; } -UDPConnection * -QUICPacket::udp_con() const +Ptr +QUICVersionNegotiationPacket::header_block() const { - return this->_udp_con; + Ptr block; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + // Overwrite the first byte + buf[0] = 0x80 | rand(); + + block->fill(written_len); + + return block; } -/** - * When packet is "Short Header Packet", QUICPacket::type() will return 1-RTT Protected (key phase 0) - * or 1-RTT Protected (key phase 1) - */ -QUICPacketType -QUICPacket::type() const +Ptr +QUICVersionNegotiationPacket::payload_block() const { - return this->_header->type(); + Ptr block; + uint8_t *buf; + size_t written_len = 0; + size_t n; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(sizeof(QUICVersion) * (this->_nversions + 1), BUFFER_SIZE_INDEX_32K)); + buf = reinterpret_cast(block->start()); + + for (auto i = 0; i < this->_nversions; ++i) { + QUICTypeUtil::write_QUICVersion(*(this->_versions + i), buf + written_len, &n); + written_len += n; + } + + // [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); + written_len += n; + + block->fill(written_len); + + return block; } -QUICConnectionId -QUICPacket::destination_cid() const +const QUICVersion * +QUICVersionNegotiationPacket::versions() const { - return this->_header->destination_cid(); + return this->_versions; } -QUICConnectionId -QUICPacket::source_cid() const +int +QUICVersionNegotiationPacket::nversions() const { - return this->_header->source_cid(); + return this->_nversions; } -QUICPacketNumber -QUICPacket::packet_number() const +// +// QUICVersionNegotiationPacketR +// +QUICVersionNegotiationPacketR::QUICVersionNegotiationPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, + Ptr blocks) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) { - return this->_header->packet_number(); + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + this->_versions = raw_buf + offset; + this->_nversions = (len - offset) / sizeof(QUICVersion); + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; } -bool -QUICPacket::is_crypto_packet() const +QUICPacketType +QUICVersionNegotiationPacketR::type() const { - return this->_header->is_crypto_packet(); + return QUICPacketType::VERSION_NEGOTIATION; } -const QUICPacketHeader & -QUICPacket::header() const +QUICPacketNumber +QUICVersionNegotiationPacketR::packet_number() const { - return *this->_header; + ink_assert(!"You should not need packet number of Version Negotiation Packet"); + return 0; } -const uint8_t * -QUICPacket::payload() const +QUICConnectionId +QUICVersionNegotiationPacketR::destination_cid() const { - return this->_payload.get(); + return this->_dcid; } -QUICVersion -QUICPacket::version() const +Ptr +QUICVersionNegotiationPacketR::header_block() const { - return this->_header->version(); + return this->_header_block; } -bool -QUICPacket::is_ack_eliciting() const +Ptr +QUICVersionNegotiationPacketR::payload_block() const { - return this->_is_ack_eliciting; + return this->_payload_block; } -bool -QUICPacket::is_probing_packet() const +const QUICVersion +QUICVersionNegotiationPacketR::supported_version(uint8_t index) const { - return this->_is_probing_packet; + return QUICTypeUtil::read_QUICVersion(this->_versions + sizeof(QUICVersion) * index); } -uint16_t -QUICPacket::size() const +int +QUICVersionNegotiationPacketR::nversions() const { - // This includes not only header size and payload size but also AEAD tag length - uint16_t size = this->_header->packet_size(); - if (size == 0) { - size = this->header_size() + this->payload_length(); - } - return size; + return this->_nversions; } -uint16_t -QUICPacket::header_size() const +// +// QUICInitialPacket +// +QUICInitialPacket::QUICInitialPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t token_len, + ats_unique_buf token, size_t length, QUICPacketNumber packet_number, bool ack_eliciting, + bool probing, bool crypto) + : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, crypto), + _token_len(token_len), + _token(std::move(token)), + _packet_number(packet_number) { - return this->_header->size(); } -uint16_t -QUICPacket::payload_length() const +QUICPacketType +QUICInitialPacket::type() const { - return this->_payload_size; + return QUICPacketType::INITIAL; } QUICKeyPhase -QUICPacket::key_phase() const +QUICInitialPacket::key_phase() const { - return this->_header->key_phase(); + return QUICKeyPhase::INITIAL; } -void -QUICPacket::store(uint8_t *buf, size_t *len) const +QUICPacketNumber +QUICInitialPacket::packet_number() const { - memcpy(buf, this->_header->buf(), this->_header->size()); - memcpy(buf + this->_header->size(), this->payload(), this->payload_length()); - *len = this->_header->size() + this->payload_length(); + return this->_packet_number; } -uint8_t -QUICPacket::calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base) +Ptr +QUICInitialPacket::header_block() const { - uint64_t d = (num - base) * 2; - uint8_t len = 0; + Ptr block; + size_t written_len = 0; + size_t n; - if (d > 0xFFFFFF) { - len = 4; - } else if (d > 0xFFFF) { - len = 3; - } else if (d > 0xFF) { - len = 2; + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + // Token Length + QUICIntUtil::write_QUICVariableInt(this->_token_len, buf + written_len, &n); + written_len += n; + + // Token + memcpy(buf + written_len, this->_token.get(), this->_token_len); + written_len += this->_token_len; + + QUICPacketNumber pn = 0; + size_t pn_len = 4; + QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); + + if (pn > 0x7FFFFF) { + pn_len = 4; + } else if (pn > 0x7FFF) { + pn_len = 3; + } else if (pn > 0x7F) { + pn_len = 2; } else { - len = 1; + pn_len = 1; } - return len; + // PN Len + QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); + + // Length + QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); + written_len += n; + + // PN Field + QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); + written_len += n; + + block->fill(written_len); + + return block; +} + +Ptr +QUICInitialPacket::payload_block() const +{ + return this->_payload_block; +} + +void +QUICInitialPacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICInitialPacketR +// +QUICInitialPacketR::QUICInitialPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Token Length Field + uint64_t token_len = QUICIntUtil::read_QUICVariableInt(raw_buf + offset, len - offset); + offset += QUICVariableInt::size(raw_buf + offset); + + // Token Field + if (token_len) { + this->_token = new QUICAddressValidationToken(raw_buf + offset, token_len); + offset += token_len; + } else { + this->_token = new QUICAddressValidationToken(nullptr, 0); + } + + // Length Field + offset += QUICVariableInt::size(raw_buf + offset); + + // PN Field + int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, + base_packet_number); + offset += pn_len; + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; +} + +QUICInitialPacketR::~QUICInitialPacketR() +{ + delete this->_token; +} + +QUICPacketType +QUICInitialPacketR::type() const +{ + return QUICPacketType::INITIAL; +} + +QUICPacketNumber +QUICInitialPacketR::packet_number() const +{ + return this->_packet_number; +} + +QUICKeyPhase +QUICInitialPacketR::key_phase() const +{ + return QUICKeyPhase::INITIAL; +} + +Ptr +QUICInitialPacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICInitialPacketR::payload_block() const +{ + return this->_payload_block; +} + +void +QUICInitialPacketR::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; +} + +const QUICAddressValidationToken & +QUICInitialPacketR::token() const +{ + return *(this->_token); } bool -QUICPacket::encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len) +QUICInitialPacketR::token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, const uint8_t *packet, + size_t packet_len) { - uint64_t mask = 0; - switch (len) { - case 1: - mask = 0xFF; - break; - case 2: - mask = 0xFFFF; - break; - case 3: - mask = 0xFFFFFF; - break; - case 4: - mask = 0xFFFFFFFF; - break; - default: - ink_assert(!"len must be 1, 2, or 4"); + QUICPacketType type = QUICPacketType::UNINITIALIZED; + QUICPacketR::type(type, packet, packet_len); + + ink_assert(type == QUICPacketType::INITIAL); + + uint8_t dcil, scil; + QUICInvariants::dcil(dcil, packet, packet_len); + QUICInvariants::scil(scil, packet, packet_len); + + token_length_filed_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; + if (token_length_filed_offset >= packet_len) { return false; } - dst = src & mask; + + token_length = QUICIntUtil::read_QUICVariableInt(packet + token_length_filed_offset, packet_len - token_length_filed_offset); + field_len = QUICVariableInt::size(packet + token_length_filed_offset); return true; } -bool -QUICPacket::decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked) +// +// QUICZeroRttPacket +// +QUICZeroRttPacket::QUICZeroRttPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing) + : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, false), _packet_number(packet_number) { - ink_assert(len == 1 || len == 2 || len == 3 || len == 4); +} - uint64_t maximum_diff = 0; - switch (len) { - case 1: - maximum_diff = 0x100; - break; - case 2: - maximum_diff = 0x10000; - break; - case 3: - maximum_diff = 0x1000000; - break; - case 4: - maximum_diff = 0x100000000; - break; - default: - ink_assert(!"len must be 1, 2, 3 or 4"); +QUICPacketType +QUICZeroRttPacket::type() const +{ + return QUICPacketType::ZERO_RTT_PROTECTED; +} + +QUICKeyPhase +QUICZeroRttPacket::key_phase() const +{ + return QUICKeyPhase::ZERO_RTT; +} + +QUICPacketNumber +QUICZeroRttPacket::packet_number() const +{ + return this->_packet_number; +} + +Ptr +QUICZeroRttPacket::header_block() const +{ + Ptr block; + size_t written_len = 0; + size_t n; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + QUICPacketNumber pn = 0; + size_t pn_len = 4; + QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); + + if (pn > 0x7FFFFF) { + pn_len = 4; + } else if (pn > 0x7FFF) { + pn_len = 3; + } else if (pn > 0x7F) { + pn_len = 2; + } else { + pn_len = 1; } - QUICPacketNumber base = largest_acked & (~(maximum_diff - 1)); - QUICPacketNumber candidate1 = base + src; - QUICPacketNumber candidate2 = base + src + maximum_diff; - QUICPacketNumber expected = largest_acked + 1; - if (((candidate1 > expected) ? (candidate1 - expected) : (expected - candidate1)) < - ((candidate2 > expected) ? (candidate2 - expected) : (expected - candidate2))) { - dst = candidate1; + // PN Len + QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); + + // Length + QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); + written_len += n; + + // PN Field + QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); + written_len += n; + + block->fill(written_len); + + return block; +} + +Ptr +QUICZeroRttPacket::payload_block() const +{ + return this->_payload_block; +} + +void +QUICZeroRttPacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICZeroRttPacketR +// +QUICZeroRttPacketR::QUICZeroRttPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Length Field + offset += QUICVariableInt::size(raw_buf + offset); + + // PN Field + int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, + base_packet_number); + offset += pn_len; + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; +} + +QUICPacketType +QUICZeroRttPacketR::type() const +{ + return QUICPacketType::ZERO_RTT_PROTECTED; +} + +QUICPacketNumber +QUICZeroRttPacketR::packet_number() const +{ + return this->_packet_number; +} + +QUICKeyPhase +QUICZeroRttPacketR::key_phase() const +{ + return QUICKeyPhase::ZERO_RTT; +} + +Ptr +QUICZeroRttPacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICZeroRttPacketR::payload_block() const +{ + return this->_payload_block; +} + +void +QUICZeroRttPacketR::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; +} + +// +// QUICHandshakePacket +// +QUICHandshakePacket::QUICHandshakePacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing, bool crypto) + : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, crypto), _packet_number(packet_number) +{ +} + +QUICPacketType +QUICHandshakePacket::type() const +{ + return QUICPacketType::HANDSHAKE; +} + +QUICKeyPhase +QUICHandshakePacket::key_phase() const +{ + return QUICKeyPhase::HANDSHAKE; +} + +QUICPacketNumber +QUICHandshakePacket::packet_number() const +{ + return this->_packet_number; +} + +Ptr +QUICHandshakePacket::header_block() const +{ + Ptr block; + size_t written_len = 0; + size_t n; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + QUICPacketNumber pn = 0; + size_t pn_len = 4; + QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); + + if (pn > 0x7FFFFF) { + pn_len = 4; + } else if (pn > 0x7FFF) { + pn_len = 3; + } else if (pn > 0x7F) { + pn_len = 2; } else { - dst = candidate2; + pn_len = 1; } - return true; + // PN Len + QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); + + // Length + QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); + written_len += n; + + // PN Field + QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); + written_len += n; + + block->fill(written_len); + + return block; +} + +Ptr +QUICHandshakePacket::payload_block() const +{ + return this->_payload_block; +} + +void +QUICHandshakePacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICHandshakePacketR +// +QUICHandshakePacketR::QUICHandshakePacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Length Field + offset += QUICVariableInt::size(raw_buf + offset); + + // PN Field + int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, + base_packet_number); + offset += pn_len; + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; +} + +QUICPacketType +QUICHandshakePacketR::type() const +{ + return QUICPacketType::HANDSHAKE; +} + +QUICKeyPhase +QUICHandshakePacketR::key_phase() const +{ + return QUICKeyPhase::HANDSHAKE; +} + +QUICPacketNumber +QUICHandshakePacketR::packet_number() const +{ + return this->_packet_number; +} + +Ptr +QUICHandshakePacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICHandshakePacketR::payload_block() const +{ + return this->_payload_block; +} + +void +QUICHandshakePacketR::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; +} + +// +// QUICRetryPacket +// +QUICRetryPacket::QUICRetryPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, QUICRetryToken &token) + : QUICLongHeaderPacket(version, dcid, scid, false, false, false), _token(token) +{ +} + +QUICPacketType +QUICRetryPacket::type() const +{ + return QUICPacketType::RETRY; +} + +QUICPacketNumber +QUICRetryPacket::packet_number() const +{ + ink_assert(!"You should not need packet number of Retry Packet"); + return 0; +} + +uint16_t +QUICRetryPacket::payload_length() const +{ + uint16_t size = 0; + + for (auto b = this->payload_block(); b; b = b->next) { + size += b->size(); + } + + return size; +} + +Ptr +QUICRetryPacket::header_block() const +{ + Ptr block; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + block->fill(written_len); + + return block; +} + +Ptr +QUICRetryPacket::payload_block() const +{ + Ptr block; + uint8_t *buf; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(QUICConnectionId::MAX_LENGTH + this->_token.length() + QUICRetryIntegrityTag::LEN, + BUFFER_SIZE_INDEX_32K)); + buf = reinterpret_cast(block->start()); + + // Retry Token + memcpy(buf + written_len, this->_token.buf(), this->_token.length()); + written_len += this->_token.length(); + block->fill(written_len); + + // Retry Integrity Tag + QUICRetryIntegrityTag::compute(buf + written_len, this->_token.original_dcid(), this->header_block(), block); + written_len += QUICRetryIntegrityTag::LEN; + block->fill(QUICRetryIntegrityTag::LEN); + + return block; +} + +const QUICRetryToken & +QUICRetryPacket::token() const +{ + return this->_token; +} + +// +// QUICRetryPacketR +// +QUICRetryPacketR::QUICRetryPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Retry Token field + this->_token = new QUICRetryToken(raw_buf + offset, len - offset - QUICRetryIntegrityTag::LEN); + offset += this->_token->length(); + + // Retry Integrity Tag + memcpy(this->_integrity_tag, raw_buf + offset, QUICRetryIntegrityTag::LEN); + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; + this->_payload_block_without_tag = this->_payload_block->clone(); + this->_payload_block_without_tag->_end = this->_payload_block_without_tag->_end - QUICRetryIntegrityTag::LEN; +} + +QUICRetryPacketR::~QUICRetryPacketR() +{ + delete this->_token; +} + +Ptr +QUICRetryPacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICRetryPacketR::payload_block() const +{ + return this->_payload_block; +} + +QUICPacketType +QUICRetryPacketR::type() const +{ + return QUICPacketType::RETRY; +} + +QUICPacketNumber +QUICRetryPacketR::packet_number() const +{ + return 0; +} + +const QUICAddressValidationToken & +QUICRetryPacketR::token() const +{ + return *(this->_token); +} + +bool +QUICRetryPacketR::has_valid_tag(QUICConnectionId &odcid) const +{ + uint8_t tag_computed[QUICRetryIntegrityTag::LEN]; + QUICRetryIntegrityTag::compute(tag_computed, 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 5fc834f45c1..9d20b234fcb 100644 --- a/iocore/net/quic/QUICPacket.h +++ b/iocore/net/quic/QUICPacket.h @@ -27,394 +27,524 @@ #include #include -#include "tscore/List.h" #include "I_IOBuffer.h" #include "QUICTypes.h" -#include "QUICHandshakeProtocol.h" -#include "QUICPacketHeaderProtector.h" -#include "QUICFrame.h" +#include "QUICRetryIntegrityTag.h" #define QUIC_FIELD_OFFSET_CONNECTION_ID 1 #define QUIC_FIELD_OFFSET_PACKET_NUMBER 4 #define QUIC_FIELD_OFFSET_PAYLOAD 5 class UDPConnection; -class QUICPacketHeader; -class QUICPacket; -class QUICPacketLongHeader; -class QUICPacketShortHeader; -extern ClassAllocator quicPacketAllocator; -extern ClassAllocator quicPacketLongHeaderAllocator; -extern ClassAllocator quicPacketShortHeaderAllocator; - -using QUICPacketHeaderDeleterFunc = void (*)(QUICPacketHeader *p); -using QUICPacketHeaderUPtr = std::unique_ptr; - -class QUICPacketHeader +class QUICPacket { public: - QUICPacketHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base) - : _from(from), _to(to), _buf(std::move(buf)), _buf_len(len), _base_packet_number(base) - { - } - ~QUICPacketHeader() {} - const uint8_t *buf(); + static constexpr int MAX_INSTANCE_SIZE = 1024; - virtual bool is_crypto_packet() const; + // Token field in Initial packet could be very long. + static constexpr size_t MAX_PACKET_HEADER_LEN = 256; - const IpEndpoint &from() const; - const IpEndpoint &to() const; + /** + * Creates a QUICPacket for sending packets + */ + QUICPacket(bool ack_eliciting, bool probing); - virtual QUICPacketType type() const = 0; + virtual ~QUICPacket(); - /* - * Returns a connection id - */ + virtual QUICPacketType type() const = 0; virtual QUICConnectionId destination_cid() const = 0; - virtual QUICConnectionId source_cid() const = 0; + virtual QUICPacketNumber packet_number() const = 0; + bool is_ack_eliciting() const; + bool is_probing_packet() const; - virtual QUICPacketNumber packet_number() const = 0; - virtual QUICVersion version() const = 0; + // TODO These two should be pure virtual + virtual Ptr + header_block() const + { + return Ptr(); + }; + virtual Ptr + payload_block() const + { + return Ptr(); + }; /* - * Returns a pointer for the payload + * Size of whole QUIC packet (header + payload + integrity check) */ - virtual const uint8_t *payload() const = 0; + virtual uint16_t size() const; /* - * Returns its payload size based on header length and buffer size that is specified to the constructo. + * Size of header */ - uint16_t payload_size() const; + virtual uint16_t header_size() const; /* - * Returns its header size + * Length of payload (payload + integrity check if exists) */ - virtual uint16_t size() const = 0; + virtual uint16_t payload_length() const; - /* - * Returns its packet size + /** + * Key phase */ - uint16_t packet_size() const; + virtual QUICKeyPhase key_phase() const; - /* - * Returns a key phase - */ - virtual QUICKeyPhase key_phase() const = 0; + // FIXME Remove this and use IOBufferBlock instead + void store(uint8_t *buf, size_t *len) const; - /* - * Stores serialized header - * - * The serialized data doesn't contain a payload part even if it was created with a buffer that contains payload data. - */ - virtual void store(uint8_t *buf, size_t *len) const = 0; + /***** STATIC MEMBERS *****/ - QUICPacketHeaderUPtr clone() const; + static uint8_t calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base); + static bool encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len); + static bool decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked); - virtual bool has_version() const = 0; - virtual bool is_valid() const = 0; +protected: + QUICPacket(); - /***** STATIC members *****/ +private: + bool _is_ack_eliciting = false; + bool _is_probing_packet = false; +}; - /* - * Load data from a buffer and create a QUICPacketHeader - * - * This creates either a QUICPacketShortHeader or a QUICPacketLongHeader. +class QUICPacketR : public QUICPacket +{ +public: + /** + * Creates a QUICPacket for receiving packets */ - static QUICPacketHeaderUPtr load(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base); + QUICPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to); - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketLongHeader. - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, - QUICConnectionId source_cid, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, ats_unique_buf payload, - size_t len); + virtual QUICPacketType type() const override = 0; - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketLongHeader for INITIAL packet - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, - QUICConnectionId source_cid, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, ats_unique_buf payload, - size_t len, ats_unique_buf token, size_t token_len); + UDPConnection *udp_con() const; + virtual const IpEndpoint &from() const; + virtual const IpEndpoint &to() const; - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketLongHeader for RETRY packet - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, - QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICConnectionId original_dcid, - ats_unique_buf retry_token, size_t retry_token_len); + static bool read_essential_info(Ptr block, QUICPacketType &type, QUICVersion &version, QUICConnectionId &dcid, + QUICConnectionId &scid, QUICPacketNumber &packet_number, QUICPacketNumber base_packet_number, + QUICKeyPhase &key_phase); + static bool type(QUICPacketType &type, const uint8_t *packet, size_t packet_len); - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketShortHeader that contains a ConnectionID. - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len); +protected: + Ptr _header_block; + Ptr _payload_block; - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketShortHeader that doesn't contain a ConnectionID (Stateless Reset Packet). +private: + UDPConnection *_udp_con = nullptr; + const IpEndpoint _from = {}; + const IpEndpoint _to = {}; +}; + +using QUICPacketDeleterFunc = void (*)(QUICPacket *p); +using QUICPacketUPtr = std::unique_ptr; + +class QUICPacketDeleter +{ +public: + static void + delete_null_packet(QUICPacket *packet) + { + ink_assert(packet == nullptr); + } + + static void + delete_dont_free(QUICPacket *packet) + { + packet->~QUICPacket(); + } + + static void + delete_packet_new(QUICPacket *packet) + { + delete packet; + } +}; + +class QUICLongHeaderPacket : public QUICPacket +{ +public: + /** + * For sending packet */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId connection_id, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, ats_unique_buf payload, - size_t len); + QUICLongHeaderPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, bool ack_eliciting, bool probing, + bool crypto); + + QUICConnectionId source_cid() const; + + QUICConnectionId destination_cid() const override; + uint16_t payload_length() const override; + virtual QUICVersion version() const; + virtual bool is_crypto_packet() const; protected: - QUICPacketHeader(){}; - QUICPacketHeader(QUICPacketType type, QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, bool has_version, - QUICVersion version, ats_unique_buf payload, size_t payload_length, QUICKeyPhase key_phase) - : _payload(std::move(payload)), - _type(type), - _key_phase(key_phase), - _packet_number(packet_number), - _base_packet_number(base_packet_number), - _version(version), - _payload_length(payload_length), - _has_version(has_version){}; - // Token field in Initial packet could be very long. - static constexpr size_t MAX_PACKET_HEADER_LEN = 256; + size_t _write_common_header(uint8_t *buf) const; - const IpEndpoint _from = {}; - const IpEndpoint _to = {}; - - // These two are used only if the instance was created with a buffer - ats_unique_buf _buf = {nullptr}; - size_t _buf_len = 0; - - // These are used only if the instance was created without a buffer - uint8_t _serialized[MAX_PACKET_HEADER_LEN]; - ats_unique_buf _payload = ats_unique_buf(nullptr); - QUICPacketType _type = QUICPacketType::UNINITIALIZED; - QUICKeyPhase _key_phase = QUICKeyPhase::INITIAL; - QUICConnectionId _connection_id = QUICConnectionId::ZERO(); - QUICPacketNumber _packet_number = 0; - QUICPacketNumber _base_packet_number = 0; - QUICVersion _version = 0; - size_t _payload_length = 0; - bool _has_version = false; + Ptr _payload_block; + size_t _payload_length = 0; + +private: + QUICVersion _version; + QUICConnectionId _dcid; + QUICConnectionId _scid; + + bool _is_crypto_packet; }; -class QUICPacketLongHeader : public QUICPacketHeader +class QUICLongHeaderPacketR : public QUICPacketR { public: - QUICPacketLongHeader() : QUICPacketHeader(){}; - virtual ~QUICPacketLongHeader(){}; - QUICPacketLongHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base); - QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &destination_cid, - const QUICConnectionId &source_cid, QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, - QUICVersion version, bool crypto, ats_unique_buf buf, size_t len, - ats_unique_buf token = ats_unique_buf(nullptr), size_t token_len = 0); - QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, const QUICConnectionId &destination_cid, - const QUICConnectionId &source_cid, const QUICConnectionId &original_dcid, ats_unique_buf retry_token, - size_t retry_token_len); + /** + * For receiving packet + */ + QUICLongHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); + + virtual ~QUICLongHeaderPacketR(){}; - QUICPacketType type() const override; QUICConnectionId destination_cid() const override; - QUICConnectionId source_cid() const override; - QUICConnectionId original_dcid() const; - QUICPacketNumber packet_number() const override; - bool has_version() const override; - bool is_valid() const override; - bool is_crypto_packet() const override; - QUICVersion version() const override; - const uint8_t *payload() const override; - const uint8_t *token() const; - size_t token_len() const; - QUICKeyPhase key_phase() const override; - uint16_t size() const override; - void store(uint8_t *buf, size_t *len) const override; + QUICConnectionId source_cid() const; + virtual QUICVersion version() const; static bool type(QUICPacketType &type, const uint8_t *packet, size_t packet_len); static bool version(QUICVersion &version, const uint8_t *packet, size_t packet_len); - static bool dcil(uint8_t &dcil, const uint8_t *packet, size_t packet_len); - static bool scil(uint8_t &scil, const uint8_t *packet, size_t packet_len); - static bool token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, const uint8_t *packet, - size_t packet_len); + static bool key_phase(QUICKeyPhase &key_phase, const uint8_t *packet, size_t packet_len); static bool length(size_t &length, uint8_t &length_field_len, size_t &length_field_offset, const uint8_t *packet, size_t packet_len); - static bool key_phase(QUICKeyPhase &key_phase, const uint8_t *packet, size_t packet_len); + static bool packet_length(size_t &packet_len, const uint8_t *buf, size_t buf_len); static bool packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len); - static bool packet_length(size_t &length, const uint8_t *buf, size_t buf_len); -private: - QUICConnectionId _destination_cid = QUICConnectionId::ZERO(); - QUICConnectionId _source_cid = QUICConnectionId::ZERO(); - QUICConnectionId _original_dcid = QUICConnectionId::ZERO(); //< RETRY packet only - size_t _token_len = 0; //< INITIAL packet only - size_t _token_offset = 0; //< INITIAL packet only - ats_unique_buf _token = ats_unique_buf(nullptr); //< INITIAL packet only - size_t _payload_offset = 0; - bool _is_crypto_packet = false; +protected: + QUICVersion _version; + QUICConnectionId _scid; + QUICConnectionId _dcid; }; -class QUICPacketShortHeader : public QUICPacketHeader +class QUICShortHeaderPacket : public QUICPacket { public: - QUICPacketShortHeader() : QUICPacketHeader(){}; - virtual ~QUICPacketShortHeader(){}; - QUICPacketShortHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base); - QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf buf, size_t len); - QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &connection_id, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, ats_unique_buf buf, size_t len); + /** + * For sending packet + */ + QUICShortHeaderPacket(QUICConnectionId dcid, QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, + QUICKeyPhase key_phase, bool ack_eliciting, bool probing); + QUICPacketType type() const override; - QUICConnectionId destination_cid() const override; - QUICConnectionId - source_cid() const override - { - return QUICConnectionId::ZERO(); - } + QUICKeyPhase key_phase() const override; QUICPacketNumber packet_number() const override; - bool has_version() const override; - bool is_valid() const override; - QUICVersion version() const override; - const uint8_t *payload() const override; + QUICConnectionId destination_cid() const override; + + uint16_t payload_length() const override; + Ptr header_block() const override; + Ptr payload_block() const override; + + void attach_payload(Ptr payload, bool unprotected); + +private: + QUICConnectionId _dcid; + QUICPacketNumber _packet_number; + QUICKeyPhase _key_phase; + int _packet_number_len; + + Ptr _payload_block; + size_t _payload_length; +}; + +class QUICShortHeaderPacketR : public QUICPacketR +{ +public: + /** + * For receiving packet + */ + QUICShortHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); + + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; QUICKeyPhase key_phase() const override; - uint16_t size() const override; - void store(uint8_t *buf, size_t *len) const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; - static bool key_phase(QUICKeyPhase &key_phase, const uint8_t *packet, size_t packet_len); static bool packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len, int dcil); private: + QUICKeyPhase _key_phase; + QUICPacketNumber _packet_number; int _packet_number_len; + QUICConnectionId _dcid; }; -class QUICPacketHeaderDeleter +class QUICStatelessResetPacket : public QUICPacket { public: - static void - delete_null_header(QUICPacketHeader *header) - { - ink_assert(header == nullptr); - } + /** + * For sending packet + */ + QUICStatelessResetPacket(QUICStatelessResetToken token, size_t maximum_size); - static void - delete_long_header(QUICPacketHeader *header) - { - QUICPacketLongHeader *long_header = dynamic_cast(header); - ink_assert(long_header != nullptr); - long_header->~QUICPacketLongHeader(); - quicPacketLongHeaderAllocator.free(long_header); - } + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; - static void - delete_short_header(QUICPacketHeader *header) - { - QUICPacketShortHeader *short_header = dynamic_cast(header); - ink_assert(short_header != nullptr); - short_header->~QUICPacketShortHeader(); - quicPacketShortHeaderAllocator.free(short_header); - } + Ptr header_block() const override; + Ptr payload_block() const override; + + QUICStatelessResetToken token() const; + +private: + QUICStatelessResetToken _token; + size_t _maximum_size; }; -class QUICPacket +class QUICStatelessResetPacketR : public QUICPacketR { public: - QUICPacket(); + /** + * For receiving packet + */ + QUICStatelessResetPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); - /* - * Creates a QUICPacket with a QUICPacketHeader and a buffer that contains payload - * - * This will be used for receiving packets. Therefore, it is expected that payload is already decrypted. - * However, QUICPacket class itself doesn't care about whether the payload is protected (encrypted) or not. + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; +}; + +class QUICVersionNegotiationPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet */ - QUICPacket(UDPConnection *udp_con, QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len); + QUICVersionNegotiationPacket(QUICConnectionId dcid, QUICConnectionId scid, const QUICVersion versions[], int nversions); - QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, std::vector &frames); + QUICPacketType type() const override; + QUICVersion version() const override; + QUICPacketNumber packet_number() const override; + uint16_t payload_length() const override; - /* - * Creates a QUICPacket with a QUICPacketHeader, a buffer that contains payload and a flag that indicates whether the packet is - * ack_eliciting - * - * This will be used for sending packets. Therefore, it is expected that payload is already encrypted. - * However, QUICPacket class itself doesn't care about whether the payload is protected (encrypted) or not. + Ptr header_block() const override; + Ptr payload_block() const override; + + const QUICVersion *versions() const; + int nversions() const; + +private: + const QUICVersion *_versions; + int _nversions; +}; + +class QUICVersionNegotiationPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet */ - QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, bool ack_eliciting, bool probing); + QUICVersionNegotiationPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); - QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, bool ack_eliciting, bool probing, - std::vector &frames); + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; - virtual ~QUICPacket(); + Ptr header_block() const override; + Ptr payload_block() const override; - UDPConnection *udp_con() const; - virtual const IpEndpoint &from() const; - virtual const IpEndpoint &to() const; - QUICPacketType type() const; - QUICConnectionId destination_cid() const; - QUICConnectionId source_cid() const; - QUICPacketNumber packet_number() const; - QUICVersion version() const; - const QUICPacketHeader &header() const; - const uint8_t *payload() const; - bool is_ack_eliciting() const; - bool is_crypto_packet() const; - bool is_probing_packet() const; + const QUICVersion supported_version(uint8_t index) const; + int nversions() const; - /* - * Size of whole QUIC packet (header + payload + integrity check) +private: + QUICConnectionId _dcid; + uint8_t *_versions; + int _nversions; +}; + +class QUICInitialPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet */ - uint16_t size() const; + QUICInitialPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t token_len, ats_unique_buf token, + size_t length, QUICPacketNumber packet_number, bool ack_eliciting, bool probing, bool crypto); - /* - * Size of header + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + +private: + size_t _token_len = 0; + ats_unique_buf _token = ats_unique_buf(nullptr); + QUICPacketNumber _packet_number; +}; + +class QUICInitialPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet */ - uint16_t header_size() const; + QUICInitialPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); + ~QUICInitialPacketR(); - /* - * Length of payload + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; + + const QUICAddressValidationToken &token() const; + + static bool token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, const uint8_t *packet, + size_t packet_len); + +protected: + Ptr _payload_block; + +private: + QUICPacketNumber _packet_number; + QUICAddressValidationToken *_token = nullptr; + + bool _parse(); +}; + +class QUICZeroRttPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet */ - uint16_t payload_length() const; + QUICZeroRttPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing); - void store(uint8_t *buf, size_t *len) const; - QUICKeyPhase key_phase() const; + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; - /***** STATIC MEMBERS *****/ + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); - static uint8_t calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base); - static bool encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len); - static bool decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked); +private: + QUICPacketNumber _packet_number; +}; + +class QUICZeroRttPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet + */ + QUICZeroRttPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); - LINK(QUICPacket, link); + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; private: - UDPConnection *_udp_con = nullptr; - QUICPacketHeaderUPtr _header = QUICPacketHeaderUPtr(nullptr, &QUICPacketHeaderDeleter::delete_null_header); - ats_unique_buf _payload = ats_unique_buf(nullptr); - size_t _payload_size = 0; - bool _is_ack_eliciting = false; - bool _is_probing_packet = false; + QUICPacketNumber _packet_number; }; -using QUICPacketDeleterFunc = void (*)(QUICPacket *p); -using QUICPacketUPtr = std::unique_ptr; +class QUICHandshakePacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet + */ + QUICHandshakePacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing, bool crypto); -class QUICPacketDeleter + QUICPacketType type() const override; + QUICKeyPhase key_phase() const override; + QUICPacketNumber packet_number() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + +private: + QUICPacketNumber _packet_number; +}; + +class QUICHandshakePacketR : public QUICLongHeaderPacketR { public: - // TODO Probably these methods should call destructor - static void - delete_null_packet(QUICPacket *packet) - { - ink_assert(packet == nullptr); - } + /** + * For receiving packet + */ + QUICHandshakePacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); - static void - delete_packet(QUICPacket *packet) - { - packet->~QUICPacket(); - quicPacketAllocator.free(packet); - } + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; + QUICKeyPhase key_phase() const override; + QUICPacketNumber packet_number() const override; + +private: + QUICPacketNumber _packet_number; +}; + +class QUICRetryPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet + */ + QUICRetryPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, QUICRetryToken &token); + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + uint16_t payload_length() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; + + const QUICRetryToken &token() const; + +private: + QUICRetryToken _token; + + bool _compute_retry_integrity_tag(uint8_t *out, QUICConnectionId odcid, Ptr header, + Ptr payload) const; +}; + +class QUICRetryPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet + */ + QUICRetryPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); + ~QUICRetryPacketR(); + + Ptr header_block() const override; + Ptr payload_block() const override; + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + + const QUICAddressValidationToken &token() const; + bool has_valid_tag(QUICConnectionId &odcid) const; + +private: + QUICAddressValidationToken *_token = nullptr; + uint8_t _integrity_tag[QUICRetryIntegrityTag::LEN]; + Ptr _payload_block_without_tag; }; diff --git a/iocore/net/quic/QUICPacketFactory.cc b/iocore/net/quic/QUICPacketFactory.cc index ea82f4400c9..436c68306b1 100644 --- a/iocore/net/quic/QUICPacketFactory.cc +++ b/iocore/net/quic/QUICPacketFactory.cc @@ -62,282 +62,255 @@ QUICPacketFactory::create_null_packet() } QUICPacketUPtr -QUICPacketFactory::create(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base_packet_number, QUICPacketCreationResult &result) +QUICPacketFactory::create(uint8_t *packet_buf, UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, + size_t len, QUICPacketNumber base_packet_number, QUICPacketCreationResult &result) { - size_t max_plain_txt_len = 2048; - ats_unique_buf plain_txt = ats_unique_malloc(max_plain_txt_len); - size_t plain_txt_len = 0; - - QUICPacketHeaderUPtr header = QUICPacketHeader::load(from, to, std::move(buf), len, base_packet_number); - - QUICConnectionId dcid = header->destination_cid(); - QUICConnectionId scid = header->source_cid(); - QUICVDebug(scid, dcid, "Decrypting %s packet #%" PRIu64 " using %s", QUICDebugNames::packet_type(header->type()), - header->packet_number(), QUICDebugNames::key_phase(header->key_phase())); - - if (header->has_version() && !QUICTypeUtil::is_supported_version(header->version())) { - if (header->type() == QUICPacketType::VERSION_NEGOTIATION) { - // version of VN packet is 0x00000000 - // This packet is unprotected. Just copy the payload - result = QUICPacketCreationResult::SUCCESS; - memcpy(plain_txt.get(), header->payload(), header->payload_size()); - plain_txt_len = header->payload_size(); - } else { - // We can't decrypt packets that have unknown versions - // What we can use is invariant field of Long Header - version, dcid, and scid - result = QUICPacketCreationResult::UNSUPPORTED; - } - } else { - Ptr plain = make_ptr(new_IOBufferBlock()); - Ptr protected_ibb = make_ptr(new_IOBufferBlock()); - protected_ibb->set_internal(reinterpret_cast(const_cast(header->payload())), header->payload_size(), - BUFFER_SIZE_NOT_ALLOCATED); - Ptr header_ibb = make_ptr(new_IOBufferBlock()); - header_ibb->set_internal(reinterpret_cast(const_cast(header->buf())), header->size(), - BUFFER_SIZE_NOT_ALLOCATED); - - switch (header->type()) { - case QUICPacketType::STATELESS_RESET: - case QUICPacketType::RETRY: - // These packets are unprotected. Just copy the payload - memcpy(plain_txt.get(), header->payload(), header->payload_size()); - plain_txt_len = header->payload_size(); - result = QUICPacketCreationResult::SUCCESS; - break; - case QUICPacketType::PROTECTED: - if (this->_pp_key_info.is_decryption_key_available(header->key_phase())) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); - if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; - } else { - result = QUICPacketCreationResult::FAILED; - } + QUICPacket *packet = nullptr; + + // FIXME This is temporal. Receive IOBufferBlock from the caller. + Ptr whole_data = make_ptr(new_IOBufferBlock()); + whole_data->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + memcpy(whole_data->start(), buf.get(), len); + whole_data->fill(len); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + + if (QUICPacketR::read_essential_info(whole_data, type, version, dcid, scid, packet_number, base_packet_number, key_phase)) { + QUICVDebug(scid, dcid, "Decrypting %s packet #%" PRIu64 " using %s", QUICDebugNames::packet_type(type), packet_number, + QUICDebugNames::key_phase(key_phase)); + + if (type != QUICPacketType::PROTECTED && !QUICTypeUtil::is_supported_version(version)) { + if (type == QUICPacketType::VERSION_NEGOTIATION) { + packet = new QUICVersionNegotiationPacketR(udp_con, from, to, whole_data); + result = QUICPacketCreationResult::SUCCESS; } else { - result = QUICPacketCreationResult::NOT_READY; + // We can't decrypt packets that have unknown versions + // What we can use is invariant field of Long Header - version, dcid, and scid + result = QUICPacketCreationResult::UNSUPPORTED; } - break; - case QUICPacketType::INITIAL: - if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL)) { - if (QUICTypeUtil::is_supported_version(header->version())) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); + } else { + Ptr plain; + switch (type) { + case QUICPacketType::STATELESS_RESET: + packet = new (packet_buf) QUICStatelessResetPacketR(udp_con, from, to, whole_data); + result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::RETRY: + packet = new (packet_buf) QUICRetryPacketR(udp_con, from, to, whole_data); + result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::PROTECTED: + packet = new (packet_buf) QUICShortHeaderPacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(packet->key_phase())) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; } else { result = QUICPacketCreationResult::FAILED; } } else { - result = QUICPacketCreationResult::SUCCESS; + result = QUICPacketCreationResult::NOT_READY; } - } else { - result = QUICPacketCreationResult::IGNORED; - } - break; - case QUICPacketType::HANDSHAKE: - if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::HANDSHAKE)) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); - if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::INITIAL: + packet = new (packet_buf) QUICInitialPacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL)) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); + if (plain != nullptr) { + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; + } else { + result = QUICPacketCreationResult::FAILED; + } } else { - result = QUICPacketCreationResult::FAILED; + result = QUICPacketCreationResult::IGNORED; } - } else { - result = QUICPacketCreationResult::IGNORED; - } - break; - case QUICPacketType::ZERO_RTT_PROTECTED: - if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::ZERO_RTT)) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); - if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::HANDSHAKE: + packet = new (packet_buf) QUICHandshakePacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::HANDSHAKE)) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); + if (plain != nullptr) { + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; + } else { + result = QUICPacketCreationResult::FAILED; + } } else { result = QUICPacketCreationResult::IGNORED; } - } else { - result = QUICPacketCreationResult::NOT_READY; + break; + case QUICPacketType::ZERO_RTT_PROTECTED: + packet = new (packet_buf) QUICZeroRttPacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::ZERO_RTT)) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); + if (plain != nullptr) { + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; + } else { + result = QUICPacketCreationResult::IGNORED; + } + } else { + result = QUICPacketCreationResult::NOT_READY; + } + break; + default: + result = QUICPacketCreationResult::FAILED; + break; } - break; - default: - result = QUICPacketCreationResult::FAILED; - break; } + } else { + Debug(tag.data(), "Failed to read essential field"); + uint8_t *buf = reinterpret_cast(whole_data->start()); + if (len > 16) { + Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + if (len > 32) { + Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[16 + 0], + buf[16 + 1], buf[16 + 2], buf[16 + 3], buf[16 + 4], buf[16 + 5], buf[16 + 6], buf[16 + 7], buf[16 + 8], buf[16 + 9], + buf[16 + 10], buf[16 + 11], buf[16 + 12], buf[16 + 13], buf[16 + 14], buf[16 + 15]); + } + if (len > 48) { + Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[32 + 0], + buf[32 + 1], buf[32 + 2], buf[32 + 3], buf[32 + 4], buf[32 + 5], buf[32 + 6], buf[32 + 7], buf[32 + 8], buf[32 + 9], + buf[32 + 10], buf[32 + 11], buf[32 + 12], buf[32 + 13], buf[32 + 14], buf[32 + 15]); + } + result = QUICPacketCreationResult::FAILED; } - QUICPacket *packet = nullptr; - if (result == QUICPacketCreationResult::SUCCESS || result == QUICPacketCreationResult::UNSUPPORTED) { - packet = quicPacketAllocator.alloc(); - new (packet) QUICPacket(udp_con, std::move(header), std::move(plain_txt), plain_txt_len); + if (result != QUICPacketCreationResult::SUCCESS && result != QUICPacketCreationResult::UNSUPPORTED) { + packet = nullptr; } - return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_packet); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr QUICPacketFactory::create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid) { - size_t len = sizeof(QUICVersion) * (countof(QUIC_SUPPORTED_VERSIONS) + 1); - ats_unique_buf versions(reinterpret_cast(ats_malloc(len))); - uint8_t *p = versions.get(); - - size_t n; - for (auto v : QUIC_SUPPORTED_VERSIONS) { - QUICTypeUtil::write_QUICVersion(v, p, &n); - p += n; - } - - // [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, p, &n); - p += n; - - ink_assert(len == static_cast(p - versions.get())); - // VN packet dosen't have packet number field and version field is always 0x00000000 - QUICPacketHeaderUPtr header = QUICPacketHeader::build(QUICPacketType::VERSION_NEGOTIATION, QUICKeyPhase::INITIAL, dcid, scid, - 0x00, 0x00, 0x00, false, std::move(versions), len); - - return QUICPacketFactory::_create_unprotected_packet(std::move(header)); + return QUICPacketUPtr(new QUICVersionNegotiationPacket(dcid, scid, QUIC_SUPPORTED_VERSIONS, countof(QUIC_SUPPORTED_VERSIONS)), + &QUICPacketDeleter::delete_packet_new); } QUICPacketUPtr -QUICPacketFactory::create_initial_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, - bool retransmittable, bool probing, bool crypto, ats_unique_buf token, size_t token_len) +QUICPacketFactory::create_initial_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing, bool crypto, ats_unique_buf token, size_t token_len) { QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::INITIAL); QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::INITIAL, QUICKeyPhase::INITIAL, destination_cid, source_cid, pn, base_packet_number, - this->_version, crypto, std::move(payload), len, std::move(token), token_len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); -} -QUICPacketUPtr -QUICPacketFactory::create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICConnectionId original_dcid, QUICRetryToken &token) -{ - ats_unique_buf payload = ats_unique_malloc(token.length()); - memcpy(payload.get(), token.buf(), token.length()); + QUICInitialPacket *packet = new (packet_buf) QUICInitialPacket(this->_version, destination_cid, source_cid, token_len, + std::move(token), length, pn, ack_eliciting, probing, crypto); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::RETRY, QUICKeyPhase::INITIAL, QUIC_SUPPORTED_VERSIONS[0], destination_cid, source_cid, - original_dcid, std::move(payload), token.length()); - return QUICPacketFactory::_create_unprotected_packet(std::move(header)); -} + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers + Ptr protected_payload = + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); + if (protected_payload != nullptr) { + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload + } else { + QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet"); + packet = nullptr; + } -QUICPacketUPtr -QUICPacketFactory::create_handshake_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, - bool retransmittable, bool probing, bool crypto) -{ - QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::HANDSHAKE); - QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::HANDSHAKE, QUICKeyPhase::HANDSHAKE, destination_cid, source_cid, pn, base_packet_number, - this->_version, crypto, std::move(payload), len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr -QUICPacketFactory::create_zero_rtt_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, - bool retransmittable, bool probing) +QUICPacketFactory::create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICRetryToken &token) { - QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ZERO_RTT); - QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::ZERO_RTT_PROTECTED, QUICKeyPhase::ZERO_RTT, destination_cid, source_cid, pn, - base_packet_number, this->_version, false, std::move(payload), len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); + return QUICPacketUPtr(new QUICRetryPacket(QUIC_SUPPORTED_VERSIONS[0], destination_cid, source_cid, token), + &QUICPacketDeleter::delete_packet_new); } QUICPacketUPtr -QUICPacketFactory::create_protected_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number, - ats_unique_buf payload, size_t len, bool retransmittable, bool probing) +QUICPacketFactory::create_handshake_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing, bool crypto) { - QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ONE_RTT); + QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::HANDSHAKE); QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - // TODO Key phase should be picked up from QUICHandshakeProtocol, probably - QUICPacketHeaderUPtr header = QUICPacketHeader::build(QUICPacketType::PROTECTED, QUICKeyPhase::PHASE_0, connection_id, pn, - base_packet_number, std::move(payload), len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); -} -QUICPacketUPtr -QUICPacketFactory::create_stateless_reset_packet(QUICConnectionId connection_id, QUICStatelessResetToken stateless_reset_token) -{ - constexpr uint8_t MIN_UNPREDICTABLE_FIELD_LEN = 5; - std::random_device rnd; + QUICHandshakePacket *packet = + new (packet_buf) QUICHandshakePacket(this->_version, destination_cid, source_cid, length, pn, ack_eliciting, probing, crypto); - uint8_t random_packet_number = static_cast(rnd() & 0xFF); - size_t payload_len = static_cast((rnd() & 0xFF) | (MIN_UNPREDICTABLE_FIELD_LEN + QUICStatelessResetToken::LEN)); - ats_unique_buf payload = ats_unique_malloc(payload_len); - uint8_t *naked_payload = payload.get(); - - // Generate random octets - for (int i = payload_len - 1; i >= 0; --i) { - naked_payload[i] = static_cast(rnd() & 0xFF); + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers + Ptr protected_payload = + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); + if (protected_payload != nullptr) { + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload + } else { + QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet"); + packet = nullptr; } - // Copy stateless reset token into payload - memcpy(naked_payload + payload_len - QUICStatelessResetToken::LEN, stateless_reset_token.buf(), QUICStatelessResetToken::LEN); - // KeyPhase won't be used - QUICPacketHeaderUPtr header = QUICPacketHeader::build(QUICPacketType::STATELESS_RESET, QUICKeyPhase::INITIAL, connection_id, - random_packet_number, 0, std::move(payload), payload_len); - return QUICPacketFactory::_create_unprotected_packet(std::move(header)); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr -QUICPacketFactory::_create_unprotected_packet(QUICPacketHeaderUPtr header) +QUICPacketFactory::create_zero_rtt_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing) { - ats_unique_buf cleartext = ats_unique_malloc(2048); - size_t cleartext_len = header->payload_size(); + QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ZERO_RTT); + QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); + + QUICZeroRttPacket *packet = + new (packet_buf) QUICZeroRttPacket(this->_version, destination_cid, source_cid, length, pn, ack_eliciting, probing); - memcpy(cleartext.get(), header->payload(), cleartext_len); - QUICPacket *packet = quicPacketAllocator.alloc(); - new (packet) QUICPacket(std::move(header), std::move(cleartext), cleartext_len, false, false); + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers + Ptr protected_payload = + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); + if (protected_payload != nullptr) { + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload + } else { + QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet"); + packet = nullptr; + } - return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_packet); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr -QUICPacketFactory::_create_encrypted_packet(QUICPacketHeaderUPtr header, bool retransmittable, bool probing) +QUICPacketFactory::create_short_header_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing) { - QUICConnectionId dcid = header->destination_cid(); - QUICConnectionId scid = header->source_cid(); - QUICVDebug(dcid, scid, "Encrypting %s packet #%" PRIu64 " using %s", QUICDebugNames::packet_type(header->type()), - header->packet_number(), QUICDebugNames::key_phase(header->key_phase())); - - QUICPacket *packet = nullptr; - - Ptr payload_ibb = make_ptr(new_IOBufferBlock()); - payload_ibb->set_internal(reinterpret_cast(const_cast(header->payload())), header->payload_size(), - BUFFER_SIZE_NOT_ALLOCATED); + QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ONE_RTT); + QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - Ptr header_ibb = make_ptr(new_IOBufferBlock()); - header_ibb->set_internal(reinterpret_cast(const_cast(header->buf())), header->size(), - BUFFER_SIZE_NOT_ALLOCATED); + // TODO Key phase should be picked up from QUICHandshakeProtocol, probably + QUICShortHeaderPacket *packet = + new (packet_buf) QUICShortHeaderPacket(destination_cid, pn, base_packet_number, QUICKeyPhase::PHASE_0, ack_eliciting, probing); + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers Ptr protected_payload = - this->_pp_protector.protect(header_ibb, payload_ibb, header->packet_number(), header->key_phase()); + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); if (protected_payload != nullptr) { - ats_unique_buf cipher_txt = ats_unique_malloc(protected_payload->size()); - memcpy(cipher_txt.get(), protected_payload->buf(), protected_payload->size()); - packet = quicPacketAllocator.alloc(); - new (packet) QUICPacket(std::move(header), std::move(cipher_txt), protected_payload->size(), retransmittable, probing); + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload } else { - QUICDebug(dcid, scid, "Failed to encrypt a packet"); + QUICDebug(destination_cid, QUICConnectionId::ZERO(), "Failed to encrypt a packet"); + packet = nullptr; } - return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_packet); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); +} + +QUICPacketUPtr +QUICPacketFactory::create_stateless_reset_packet(QUICStatelessResetToken stateless_reset_token, size_t maximum_size) +{ + return QUICPacketUPtr(new QUICStatelessResetPacket(stateless_reset_token, maximum_size), &QUICPacketDeleter::delete_packet_new); } void diff --git a/iocore/net/quic/QUICPacketFactory.h b/iocore/net/quic/QUICPacketFactory.h index 732ad35514d..d5316ce02f5 100644 --- a/iocore/net/quic/QUICPacketFactory.h +++ b/iocore/net/quic/QUICPacketFactory.h @@ -45,27 +45,26 @@ class QUICPacketFactory public: static QUICPacketUPtr create_null_packet(); static QUICPacketUPtr create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid); - static QUICPacketUPtr create_stateless_reset_packet(QUICConnectionId connection_id, - QUICStatelessResetToken stateless_reset_token); - static QUICPacketUPtr create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICConnectionId original_dcid, QUICRetryToken &token); + 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); QUICPacketFactory(const QUICPacketProtectionKeyInfo &pp_key_info) : _pp_key_info(pp_key_info), _pp_protector(pp_key_info) {} - QUICPacketUPtr create(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, size_t len, + QUICPacketUPtr create(uint8_t *packet_buf, UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number, QUICPacketCreationResult &result); - QUICPacketUPtr create_initial_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, bool ack_eliciting, - bool probing, bool crypto, ats_unique_buf token = ats_unique_buf(nullptr), - size_t token_len = 0); - QUICPacketUPtr create_handshake_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, + QUICPacketUPtr create_initial_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing, bool crypto, + ats_unique_buf token = ats_unique_buf(nullptr), size_t token_len = 0); + QUICPacketUPtr create_handshake_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, bool ack_eliciting, bool probing, bool crypto); - QUICPacketUPtr create_zero_rtt_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, bool ack_eliciting, - bool probing); - QUICPacketUPtr create_protected_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number, - ats_unique_buf payload, size_t len, bool ack_eliciting, bool probing); + QUICPacketUPtr create_zero_rtt_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing); + QUICPacketUPtr create_short_header_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing); void set_version(QUICVersion negotiated_version); bool is_ready_to_create_protected_packet(); @@ -79,7 +78,4 @@ class QUICPacketFactory // Initial, 0/1-RTT, and Handshake QUICPacketNumberGenerator _packet_number_generator[3]; - - static QUICPacketUPtr _create_unprotected_packet(QUICPacketHeaderUPtr header); - QUICPacketUPtr _create_encrypted_packet(QUICPacketHeaderUPtr header, bool ack_eliciting, bool probing); }; diff --git a/iocore/net/quic/QUICPacketHeaderProtector.cc b/iocore/net/quic/QUICPacketHeaderProtector.cc index 0dbfe8df2e7..f6a71b4da6d 100644 --- a/iocore/net/quic/QUICPacketHeaderProtector.cc +++ b/iocore/net/quic/QUICPacketHeaderProtector.cc @@ -32,19 +32,15 @@ bool QUICPacketHeaderProtector::protect(uint8_t *unprotected_packet, size_t unprotected_packet_len, int dcil) const { // Do nothing if the packet is VN - if (QUICInvariants::is_long_header(unprotected_packet)) { - QUICVersion version; - QUICPacketLongHeader::version(version, unprotected_packet, unprotected_packet_len); - if (version == 0x0) { - return true; - } + QUICPacketType type; + QUICPacketR::type(type, unprotected_packet, unprotected_packet_len); + if (type == QUICPacketType::VERSION_NEGOTIATION) { + return true; } QUICKeyPhase phase; - QUICPacketType type; if (QUICInvariants::is_long_header(unprotected_packet)) { - QUICPacketLongHeader::key_phase(phase, unprotected_packet, unprotected_packet_len); - QUICPacketLongHeader::type(type, unprotected_packet, unprotected_packet_len); + QUICLongHeaderPacketR::key_phase(phase, unprotected_packet, unprotected_packet_len); } else { // This is a kind of hack. For short header we need to use the same key for header protection regardless of the key phase. phase = QUICKeyPhase::PHASE_0; @@ -89,24 +85,15 @@ bool QUICPacketHeaderProtector::unprotect(uint8_t *protected_packet, size_t protected_packet_len) const { // Do nothing if the packet is VN or RETRY - if (QUICInvariants::is_long_header(protected_packet)) { - QUICVersion version; - QUICPacketLongHeader::version(version, protected_packet, protected_packet_len); - if (version == 0x0) { - return true; - } - QUICPacketType type; - QUICPacketLongHeader::type(type, protected_packet, protected_packet_len); - if (type == QUICPacketType::RETRY) { - return true; - } + QUICPacketType type; + QUICPacketR::type(type, protected_packet, protected_packet_len); + if (type == QUICPacketType::VERSION_NEGOTIATION || type == QUICPacketType::RETRY) { + return true; } QUICKeyPhase phase; - QUICPacketType type; if (QUICInvariants::is_long_header(protected_packet)) { - QUICPacketLongHeader::key_phase(phase, protected_packet, protected_packet_len); - QUICPacketLongHeader::type(type, protected_packet, protected_packet_len); + QUICLongHeaderPacketR::key_phase(phase, protected_packet, protected_packet_len); } else { // This is a kind of hack. For short header we need to use the same key for header protection regardless of the key phase. phase = QUICKeyPhase::PHASE_0; @@ -155,7 +142,7 @@ QUICPacketHeaderProtector::_calc_sample_offset(uint8_t *sample_offset, const uin size_t dummy; uint8_t length_len; size_t length_offset; - if (!QUICPacketLongHeader::length(dummy, length_len, length_offset, protected_packet, protected_packet_len)) { + if (!QUICLongHeaderPacketR::length(dummy, length_len, length_offset, protected_packet, protected_packet_len)) { return false; } @@ -175,10 +162,10 @@ QUICPacketHeaderProtector::_unprotect(uint8_t *protected_packet, size_t protecte // Unprotect packet number if (QUICInvariants::is_long_header(protected_packet)) { protected_packet[0] ^= mask[0] & 0x0f; - QUICPacketLongHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len); + QUICLongHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len); } else { protected_packet[0] ^= mask[0] & 0x1f; - QUICPacketShortHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len, QUICConnectionId::SCID_LEN); + QUICShortHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len, QUICConnectionId::SCID_LEN); } uint8_t pn_length = QUICTypeUtil::read_QUICPacketNumberLen(protected_packet); @@ -199,10 +186,10 @@ QUICPacketHeaderProtector::_protect(uint8_t *protected_packet, size_t protected_ // Protect packet number if (QUICInvariants::is_long_header(protected_packet)) { protected_packet[0] ^= mask[0] & 0x0f; - QUICPacketLongHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len); + QUICLongHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len); } else { protected_packet[0] ^= mask[0] & 0x1f; - QUICPacketShortHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len, dcil); + QUICShortHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len, dcil); } for (int i = 0; i < pn_length; ++i) { diff --git a/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc b/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc index 54c539e6c59..63e71bf0105 100644 --- a/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc +++ b/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc @@ -23,9 +23,36 @@ #include "QUICPacketHeaderProtector.h" +#include "openssl/chacha.h" + bool QUICPacketHeaderProtector::_generate_mask(uint8_t *mask, const uint8_t *sample, const uint8_t *key, const EVP_CIPHER *cipher) const { - ink_assert(!"not implemented"); - return false; + static constexpr unsigned char FIVE_ZEROS[] = {0x00, 0x00, 0x00, 0x00, 0x00}; + + if (cipher == nullptr) { + uint32_t counter = htole32(*reinterpret_cast(&sample[0])); + CRYPTO_chacha_20(mask, FIVE_ZEROS, sizeof(FIVE_ZEROS), key, &sample[4], counter); + } else { + int len = 0; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + return false; + } + if (!EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + EVP_CIPHER_CTX_free(ctx); + return false; + } + if (!EVP_EncryptUpdate(ctx, mask, &len, sample, 16)) { + EVP_CIPHER_CTX_free(ctx); + return false; + } + if (!EVP_EncryptFinal_ex(ctx, mask + len, &len)) { + EVP_CIPHER_CTX_free(ctx); + return false; + } + EVP_CIPHER_CTX_free(ctx); + } + + return true; } diff --git a/iocore/net/quic/QUICPacketHeaderProtector_legacy.cc b/iocore/net/quic/QUICPacketHeaderProtector_legacy.cc new file mode 100644 index 00000000000..43bbba8e901 --- /dev/null +++ b/iocore/net/quic/QUICPacketHeaderProtector_legacy.cc @@ -0,0 +1,53 @@ +/** @file + * + * QUIC Packet Header Protector (OpenSSL specific code) + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QUICPacketHeaderProtector.h" + +bool +QUICPacketHeaderProtector::_generate_mask(uint8_t *mask, const uint8_t *sample, const uint8_t *key, const EVP_CIPHER *cipher) const +{ + static constexpr unsigned char FIVE_ZEROS[] = {0x00, 0x00, 0x00, 0x00, 0x00}; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + + if (!ctx || !EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + return false; + } + + int len = 0; + if (cipher == EVP_chacha20()) { + if (!EVP_EncryptUpdate(ctx, mask, &len, FIVE_ZEROS, sizeof(FIVE_ZEROS))) { + return false; + } + } else { + if (!EVP_EncryptUpdate(ctx, mask, &len, sample, 16)) { + return false; + } + } + if (!EVP_EncryptFinal_ex(ctx, mask + len, &len)) { + return false; + } + + EVP_CIPHER_CTX_free(ctx); + + return true; +} diff --git a/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc b/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc index 43bbba8e901..c7d65a19e11 100644 --- a/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc +++ b/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc @@ -29,21 +29,28 @@ QUICPacketHeaderProtector::_generate_mask(uint8_t *mask, const uint8_t *sample, static constexpr unsigned char FIVE_ZEROS[] = {0x00, 0x00, 0x00, 0x00, 0x00}; EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - if (!ctx || !EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + if (!ctx) { + return false; + } + if (!EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + EVP_CIPHER_CTX_free(ctx); return false; } int len = 0; if (cipher == EVP_chacha20()) { if (!EVP_EncryptUpdate(ctx, mask, &len, FIVE_ZEROS, sizeof(FIVE_ZEROS))) { + EVP_CIPHER_CTX_free(ctx); return false; } } else { if (!EVP_EncryptUpdate(ctx, mask, &len, sample, 16)) { + EVP_CIPHER_CTX_free(ctx); return false; } } if (!EVP_EncryptFinal_ex(ctx, mask + len, &len)) { + EVP_CIPHER_CTX_free(ctx); return false; } diff --git a/iocore/net/quic/QUICPacketPayloadProtector.cc b/iocore/net/quic/QUICPacketPayloadProtector.cc index 374480b133e..4b8291c8a39 100644 --- a/iocore/net/quic/QUICPacketPayloadProtector.cc +++ b/iocore/net/quic/QUICPacketPayloadProtector.cc @@ -46,12 +46,16 @@ QUICPacketPayloadProtector::protect(const Ptr unprotected_header, const EVP_CIPHER *cipher = this->_pp_key_info.get_cipher(phase); - protected_payload = make_ptr(new_IOBufferBlock()); - protected_payload->alloc(iobuffer_size_to_index(unprotected_payload->size() + tag_len, BUFFER_SIZE_INDEX_32K)); + protected_payload = make_ptr(new_IOBufferBlock()); + size_t unprotected_payload_len = 0; + for (Ptr tmp = unprotected_payload; tmp; tmp = tmp->next) { + unprotected_payload_len += tmp->size(); + } + protected_payload->alloc(iobuffer_size_to_index(unprotected_payload_len + tag_len, BUFFER_SIZE_INDEX_32K)); size_t written_len = 0; if (!this->_protect(reinterpret_cast(protected_payload->start()), written_len, protected_payload->write_avail(), - unprotected_payload, pkt_num, reinterpret_cast(unprotected_header->buf()), + unprotected_payload, pkt_num, reinterpret_cast(unprotected_header->start()), unprotected_header->size(), key, iv, iv_len, cipher, tag_len)) { Debug(tag, "Failed to encrypt a packet #%" PRIu64 " with keys for %s", pkt_num, QUICDebugNames::key_phase(phase)); protected_payload = nullptr; @@ -84,9 +88,9 @@ QUICPacketPayloadProtector::unprotect(const Ptr unprotected_heade size_t written_len = 0; if (!this->_unprotect(reinterpret_cast(unprotected_payload->start()), written_len, unprotected_payload->write_avail(), - reinterpret_cast(protected_payload->buf()), protected_payload->size(), pkt_num, - reinterpret_cast(unprotected_header->buf()), unprotected_header->size(), key, iv, iv_len, cipher, - tag_len)) { + reinterpret_cast(protected_payload->start()), protected_payload->size(), pkt_num, + reinterpret_cast(unprotected_header->start()), unprotected_header->size(), key, iv, iv_len, + cipher, tag_len)) { Debug(tag, "Failed to decrypt a packet #%" PRIu64, pkt_num); unprotected_payload = nullptr; } else { @@ -122,3 +126,111 @@ QUICPacketPayloadProtector::_gen_nonce(uint8_t *nonce, size_t &nonce_len, uint64 nonce[iv_len - 8 + i] ^= p[i]; } } + +bool +QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const Ptr plain, + uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, const uint8_t *iv, + size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + cipher_len = 0; + for (Ptr b = plain; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->start()), b->size())) { + return false; + } + cipher_len += len; + } + + if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + return false; + } + cipher_len += len; + + if (max_cipher_len < cipher_len + tag_len) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + return false; + } + cipher_len += tag_len; + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; +} + +bool +QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, + size_t cipher_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, + const uint8_t *iv, size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + if (cipher_len < tag_len) { + return false; + } + cipher_len -= tag_len; + if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + return false; + } + plain_len = len; + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + return false; + } + + int ret = EVP_DecryptFinal_ex(aead_ctx, plain + len, &len); + + EVP_CIPHER_CTX_free(aead_ctx); + + if (ret > 0) { + plain_len += len; + return true; + } else { + Debug(tag, "Failed to decrypt -- the first 4 bytes decrypted are %0x %0x %0x %0x", plain[0], plain[1], plain[2], plain[3]); + return false; + } +} diff --git a/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc b/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc index 56e88dd45fd..0352e513d13 100644 --- a/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc +++ b/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc @@ -25,24 +25,129 @@ #include "QUICPacketPayloadProtector.h" #include "tscore/Diags.h" -// static constexpr char tag[] = "quic_ppp"; +static constexpr char tag[] = "quic_ppp"; bool -QUICPacketPayloadProtector::_protect(uint8_t *protected_payload, size_t &protected_payload_len, size_t max_protecgted_payload_len, - const Ptr plain, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, - const uint8_t *key, const uint8_t *iv, size_t iv_len, const EVP_CIPHER *cipher, - size_t tag_len) const +QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const Ptr plain, + uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, const uint8_t *iv, + size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const { - ink_assert(!"not implemented"); - return false; + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + + cipher_len = 0; + Ptr b = plain; + while (b) { + if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->buf()), b->size())) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len += len; + b = b->next; + } + + if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len += len; + + if (max_cipher_len < cipher_len + tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len += tag_len; + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; } bool -QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *protected_payload, - size_t protected_payload_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, - const uint8_t *key, const uint8_t *iv, size_t iv_len, const EVP_CIPHER *cipher, - size_t tag_len) const +QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, + size_t cipher_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, + const uint8_t *iv, size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const { - ink_assert(!"not implemented"); - return false; + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + + if (cipher_len < tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len -= tag_len; + if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + plain_len = len; + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + + int ret = EVP_DecryptFinal_ex(aead_ctx, plain + len, &len); + + EVP_CIPHER_CTX_free(aead_ctx); + + if (ret > 0) { + plain_len += len; + return true; + } else { + Debug(tag, "Failed to decrypt -- the first 4 bytes decrypted are %0x %0x %0x %0x", plain[0], plain[1], plain[2], plain[3]); + return false; + } } diff --git a/iocore/net/quic/QUICPacketPayloadProtector_legacy.cc b/iocore/net/quic/QUICPacketPayloadProtector_legacy.cc new file mode 100644 index 00000000000..5fc9e2e6e11 --- /dev/null +++ b/iocore/net/quic/QUICPacketPayloadProtector_legacy.cc @@ -0,0 +1,136 @@ +/** @file + * + * QUIC Packet Payload Protector (OpenSSL specific code) + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QUICPacketProtectionKeyInfo.h" +#include "QUICPacketPayloadProtector.h" +#include "tscore/Diags.h" + +static constexpr char tag[] = "quic_ppp"; + +bool +QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const Ptr plain, + uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, const uint8_t *iv, + size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + cipher_len = 0; + for (Ptr b = plain; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->start()), b->size())) { + return false; + } + cipher_len += len; + } + + if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + return false; + } + cipher_len += len; + + if (max_cipher_len < cipher_len + tag_len) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + return false; + } + cipher_len += tag_len; + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; +} + +bool +QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, + size_t cipher_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, + const uint8_t *iv, size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + if (cipher_len < tag_len) { + return false; + } + cipher_len -= tag_len; + if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + return false; + } + plain_len = len; + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + return false; + } + + int ret = EVP_DecryptFinal_ex(aead_ctx, plain + len, &len); + + EVP_CIPHER_CTX_free(aead_ctx); + + if (ret > 0) { + plain_len += len; + return true; + } else { + Debug(tag, "Failed to decrypt -- the first 4 bytes decrypted are %0x %0x %0x %0x", plain[0], plain[1], plain[2], plain[3]); + return false; + } +} diff --git a/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc b/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc index b25f099051e..c900718417b 100644 --- a/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc +++ b/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc @@ -43,15 +43,19 @@ QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t return false; } if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } @@ -59,6 +63,7 @@ QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t Ptr b = plain; while (b) { if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->buf()), b->size())) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len += len; @@ -66,14 +71,17 @@ QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t } if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len += len; if (max_cipher_len < cipher_len + tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len += tag_len; @@ -99,28 +107,35 @@ QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t return false; } if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (cipher_len < tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len -= tag_len; if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } plain_len = len; if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } diff --git a/iocore/net/quic/QUICPacketReceiveQueue.cc b/iocore/net/quic/QUICPacketReceiveQueue.cc index c90385bcffc..f19779099f9 100644 --- a/iocore/net/quic/QUICPacketReceiveQueue.cc +++ b/iocore/net/quic/QUICPacketReceiveQueue.cc @@ -22,6 +22,7 @@ */ #include "QUICPacketReceiveQueue.h" +#include "QUICPacketHeaderProtector.h" #include "QUICPacketFactory.h" #include "QUICIntUtil.h" @@ -44,7 +45,7 @@ QUICPacketReceiveQueue::enqueue(UDPPacket *packet) } QUICPacketUPtr -QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) +QUICPacketReceiveQueue::dequeue(uint8_t *packet_buf, QUICPacketCreationResult &result) { QUICPacketUPtr quic_packet = QUICPacketFactory::create_null_packet(); UDPPacket *udp_packet = nullptr; @@ -67,7 +68,7 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) IOBufferBlock *b = udp_packet->getIOBlockChain(); size_t written = 0; while (b) { - memcpy(this->_payload.get() + written, b->buf(), b->read_avail()); + memcpy(this->_payload.get() + written, b->start(), b->read_avail()); written += b->read_avail(); b = b->next.get(); } @@ -83,7 +84,7 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) if (QUICInvariants::is_long_header(buf)) { QUICVersion version; - QUICPacketLongHeader::version(version, buf, remaining_len); + QUICLongHeaderPacketR::version(version, buf, remaining_len); if (is_vn(version)) { pkt_len = remaining_len; type = QUICPacketType::VERSION_NEGOTIATION; @@ -91,17 +92,17 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) result = QUICPacketCreationResult::UNSUPPORTED; pkt_len = remaining_len; } else { - QUICPacketLongHeader::type(type, this->_payload.get() + this->_offset, remaining_len); + QUICLongHeaderPacketR::type(type, this->_payload.get() + this->_offset, remaining_len); if (type == QUICPacketType::RETRY) { pkt_len = remaining_len; } else { - if (!QUICPacketLongHeader::packet_length(pkt_len, this->_payload.get() + this->_offset, remaining_len)) { + if (!QUICLongHeaderPacketR::packet_length(pkt_len, this->_payload.get() + this->_offset, remaining_len)) { + // This should not happen basically. Ignore rest of data on current packet. this->_payload.release(); this->_payload = nullptr; this->_payload_len = 0; this->_offset = 0; - - result = QUICPacketCreationResult::IGNORED; + result = QUICPacketCreationResult::IGNORED; return quic_packet; } @@ -148,7 +149,7 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) } if (this->_ph_protector.unprotect(pkt.get(), pkt_len)) { - quic_packet = this->_packet_factory.create(this->_udp_con, this->_from, this->_to, std::move(pkt), pkt_len, + quic_packet = this->_packet_factory.create(packet_buf, this->_udp_con, this->_from, this->_to, std::move(pkt), pkt_len, this->_largest_received_packet_number, result); } else { // ZERO_RTT might be rejected @@ -175,7 +176,8 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) // do nothing - if the packet is unsupported version, we don't know packet number break; default: - if (quic_packet && quic_packet->packet_number() > this->_largest_received_packet_number) { + if (quic_packet && quic_packet->type() != QUICPacketType::VERSION_NEGOTIATION && + quic_packet->packet_number() > this->_largest_received_packet_number) { this->_largest_received_packet_number = quic_packet->packet_number(); } } diff --git a/iocore/net/quic/QUICPacketReceiveQueue.h b/iocore/net/quic/QUICPacketReceiveQueue.h index e4e3e297209..78e5b00a18a 100644 --- a/iocore/net/quic/QUICPacketReceiveQueue.h +++ b/iocore/net/quic/QUICPacketReceiveQueue.h @@ -29,6 +29,7 @@ #include "QUICPacket.h" class QUICPacketFactory; +class QUICPacketHeaderProtector; class QUICPacketReceiveQueue { @@ -36,7 +37,7 @@ class QUICPacketReceiveQueue QUICPacketReceiveQueue(QUICPacketFactory &packet_factory, QUICPacketHeaderProtector &ph_protector); void enqueue(UDPPacket *packet); - QUICPacketUPtr dequeue(QUICPacketCreationResult &result); + QUICPacketUPtr dequeue(uint8_t *packet_buf, QUICPacketCreationResult &result); uint32_t size(); void reset(); diff --git a/iocore/net/quic/QUICPathManager.cc b/iocore/net/quic/QUICPathManager.cc index 613efecbe19..c0adbdecc5b 100644 --- a/iocore/net/quic/QUICPathManager.cc +++ b/iocore/net/quic/QUICPathManager.cc @@ -28,7 +28,7 @@ #define QUICDebug(fmt, ...) Debug("quic_path", "[%s] " fmt, this->_cinfo.cids().data(), ##__VA_ARGS__) void -QUICPathManager::open_new_path(const QUICPath &path, ink_hrtime timeout_in) +QUICPathManagerImpl::open_new_path(const QUICPath &path, ink_hrtime timeout_in) { if (this->_verify_timeout_at == 0) { // Overwrite _previous_path only if _current_path is verified @@ -41,14 +41,14 @@ QUICPathManager::open_new_path(const QUICPath &path, ink_hrtime timeout_in) } void -QUICPathManager::set_trusted_path(const QUICPath &path) +QUICPathManagerImpl::set_trusted_path(const QUICPath &path) { this->_current_path = path; this->_previous_path = path; } void -QUICPathManager::_check_verify_timeout() +QUICPathManagerImpl::_check_verify_timeout() { if (this->_verify_timeout_at != 0) { if (this->_path_validator.is_validated(this->_current_path)) { @@ -66,14 +66,14 @@ QUICPathManager::_check_verify_timeout() } const QUICPath & -QUICPathManager::get_current_path() +QUICPathManagerImpl::get_current_path() { this->_check_verify_timeout(); return this->_current_path; } const QUICPath & -QUICPathManager::get_verified_path() +QUICPathManagerImpl::get_verified_path() { this->_check_verify_timeout(); if (this->_verify_timeout_at != 0) { diff --git a/iocore/net/quic/QUICPathManager.h b/iocore/net/quic/QUICPathManager.h index 9f9eb37b8eb..1d4542a629b 100644 --- a/iocore/net/quic/QUICPathManager.h +++ b/iocore/net/quic/QUICPathManager.h @@ -31,15 +31,25 @@ class QUICPathValidator; class QUICPathManager { public: - QUICPathManager(const QUICConnectionInfoProvider &info, QUICPathValidator &path_validator) + virtual ~QUICPathManager() {} + virtual const QUICPath &get_current_path() = 0; + virtual const QUICPath &get_verified_path() = 0; + virtual void open_new_path(const QUICPath &path, ink_hrtime timeout_in) = 0; + virtual void set_trusted_path(const QUICPath &path) = 0; +}; + +class QUICPathManagerImpl : public QUICPathManager +{ +public: + QUICPathManagerImpl(const QUICConnectionInfoProvider &info, QUICPathValidator &path_validator) : _cinfo(info), _path_validator(path_validator) { } - const QUICPath &get_current_path(); - const QUICPath &get_verified_path(); - void open_new_path(const QUICPath &path, ink_hrtime timeout_in); - void set_trusted_path(const QUICPath &path); + const QUICPath &get_current_path() override; + const QUICPath &get_verified_path() override; + void open_new_path(const QUICPath &path, ink_hrtime timeout_in) override; + void set_trusted_path(const QUICPath &path) override; private: const QUICConnectionInfoProvider &_cinfo; diff --git a/iocore/net/quic/QUICPinger.cc b/iocore/net/quic/QUICPinger.cc index 84454c8eb66..2514c028210 100644 --- a/iocore/net/quic/QUICPinger.cc +++ b/iocore/net/quic/QUICPinger.cc @@ -24,18 +24,20 @@ #include "QUICPinger.h" void -QUICPinger::request() +QUICPinger::request(QUICEncryptionLevel level) { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - ++this->_need_to_fire; + ink_assert(level != QUICEncryptionLevel::NONE); + ++this->_need_to_fire[static_cast(level)]; } void -QUICPinger::cancel() +QUICPinger::cancel(QUICEncryptionLevel level) { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - if (this->_need_to_fire > 0) { - --this->_need_to_fire; + ink_assert(level != QUICEncryptionLevel::NONE); + if (this->_need_to_fire[static_cast(level)] > 0) { + --this->_need_to_fire[static_cast(level)]; } } @@ -44,27 +46,25 @@ QUICPinger::_will_generate_frame(QUICEncryptionLevel level, size_t current_packe { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - if (level != QUICEncryptionLevel::ONE_RTT) { - return false; - } - // PING Frame is meaningless for ack_eliciting packet. Cancel it. if (ack_eliciting) { this->_ack_eliciting_packet_out = true; - this->cancel(); + this->cancel(level); return false; } - if (this->_ack_eliciting_packet_out == false && !ack_eliciting && current_packet_size > 0 && this->_need_to_fire == 0) { + ink_assert(level != QUICEncryptionLevel::NONE); + if (this->_ack_eliciting_packet_out == false && !ack_eliciting && current_packet_size > 0 && + this->_need_to_fire[static_cast(level)] == 0) { // force to send an PING Frame - this->request(); + this->request(level); } // only update `_ack_eliciting_packet_out` when we has something to send. if (current_packet_size) { this->_ack_eliciting_packet_out = ack_eliciting; } - return this->_need_to_fire > 0; + return this->_need_to_fire[static_cast(level)] > 0; } /** @@ -77,10 +77,11 @@ QUICPinger::_generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); QUICFrame *frame = nullptr; - if (level == QUICEncryptionLevel::ONE_RTT && this->_need_to_fire > 0 && maximum_frame_size > 0) { + ink_assert(level != QUICEncryptionLevel::NONE); + if (this->_need_to_fire[static_cast(level)] > 0 && maximum_frame_size > 0) { // don't care ping frame lost or acked frame = QUICFrameFactory::create_ping_frame(buf, 0, nullptr); - --this->_need_to_fire; + --this->_need_to_fire[static_cast(level)]; this->_ack_eliciting_packet_out = true; } @@ -88,8 +89,9 @@ QUICPinger::_generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* } uint64_t -QUICPinger::count() +QUICPinger::count(QUICEncryptionLevel level) { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - return this->_need_to_fire; + ink_assert(level != QUICEncryptionLevel::NONE); + return this->_need_to_fire[static_cast(level)]; } diff --git a/iocore/net/quic/QUICPinger.h b/iocore/net/quic/QUICPinger.h index 483cfb2cd0e..0ea9b58f8d2 100644 --- a/iocore/net/quic/QUICPinger.h +++ b/iocore/net/quic/QUICPinger.h @@ -35,9 +35,9 @@ class QUICPinger : public QUICFrameOnceGenerator public: QUICPinger() : _mutex(new_ProxyMutex()) {} - void request(); - void cancel(); - uint64_t count(); + void request(QUICEncryptionLevel level); + void cancel(QUICEncryptionLevel level); + uint64_t count(QUICEncryptionLevel level); private: // QUICFrameGenerator @@ -48,5 +48,5 @@ class QUICPinger : public QUICFrameOnceGenerator bool _ack_eliciting_packet_out = false; Ptr _mutex; - uint64_t _need_to_fire = 0; + uint64_t _need_to_fire[4] = {0}; // Initial, 0RTT, HANDSHAKE and 1RTT }; diff --git a/iocore/net/quic/QUICResetTokenTable.cc b/iocore/net/quic/QUICResetTokenTable.cc new file mode 100644 index 00000000000..0da753f976f --- /dev/null +++ b/iocore/net/quic/QUICResetTokenTable.cc @@ -0,0 +1,53 @@ +/** @file + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "tscore/Diags.h" +#include "QUICResetTokenTable.h" +#include "QUICConnection.h" + +void +QUICResetTokenTable::insert(const QUICStatelessResetToken token, QUICConnection *connection) +{ + Debug("quic_reset_token_table", "Token:%02x%02x%02x%02x... CID:%08" PRIx32 "...", token.buf()[0], token.buf()[1], token.buf()[2], + token.buf()[3], connection->connection_id().h32()); + this->_map.emplace(token, connection); +} + +QUICConnection * +QUICResetTokenTable::lookup(QUICStatelessResetToken token) +{ + Debug("quic_reset_token_table", "Token:%02x%02x%02x%02x...", token.buf()[0], token.buf()[1], token.buf()[2], token.buf()[3]); + auto result = this->_map.find(token); + if (result != this->_map.end()) { + Debug("quic_reset_token_table", "CID:%08" PRIx32 "...", result->second->connection_id().h32()); + return result->second; + } else { + Debug("quic_reset_token_table", "not fouund"); + return nullptr; + } +} + +void +QUICResetTokenTable::erase(const QUICStatelessResetToken token) +{ + Debug("quic_reset_token_table", "Token:%02x%02x%02x%02x...", token.buf()[0], token.buf()[1], token.buf()[2], token.buf()[3]); + this->_map.erase(token); +} diff --git a/iocore/net/quic/QUICResetTokenTable.h b/iocore/net/quic/QUICResetTokenTable.h new file mode 100644 index 00000000000..559b2647582 --- /dev/null +++ b/iocore/net/quic/QUICResetTokenTable.h @@ -0,0 +1,46 @@ +/** @file + + @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" + +class QUICConnection; + +class QUICResetTokenTable +{ +public: + void insert(const QUICStatelessResetToken token, QUICConnection *connection); + QUICConnection *lookup(const QUICStatelessResetToken token); + void erase(const QUICStatelessResetToken token); + +private: + class TokenHasher + { + public: + uint64_t + operator()(const QUICStatelessResetToken &key) const + { + return static_cast(key); + } + }; + std::unordered_map _map; +}; diff --git a/iocore/net/quic/QUICRetryIntegrityTag.cc b/iocore/net/quic/QUICRetryIntegrityTag.cc new file mode 100644 index 00000000000..c528656896d --- /dev/null +++ b/iocore/net/quic/QUICRetryIntegrityTag.cc @@ -0,0 +1,79 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QUICRetryIntegrityTag.h" + +bool +QUICRetryIntegrityTag::compute(uint8_t *out, QUICConnectionId odcid, Ptr header, Ptr payload) +{ + EVP_CIPHER_CTX *aead_ctx; + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, nullptr)) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, KEY_FOR_RETRY_INTEGRITY_TAG, NONCE_FOR_RETRY_INTEGRITY_TAG)) { + return false; + } + + // Original Destination Connection ID + size_t n; + int dummy; + uint8_t odcid_buf[1 + QUICConnectionId::MAX_LENGTH]; + // Len + odcid_buf[0] = odcid.length(); + // ID + QUICTypeUtil::write_QUICConnectionId(odcid, odcid_buf + 1, &n); + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &dummy, odcid_buf, 1 + odcid.length())) { + return false; + } + + // Retry Packet + for (Ptr b = header; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &dummy, reinterpret_cast(b->start()), b->size())) { + return false; + } + } + for (Ptr b = payload; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &dummy, reinterpret_cast(b->start()), b->size())) { + return false; + } + } + + if (!EVP_EncryptFinal_ex(aead_ctx, nullptr, &dummy)) { + return false; + } + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, LEN, out)) { + return false; + } + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; +} diff --git a/iocore/net/quic/QUICRetryIntegrityTag.h b/iocore/net/quic/QUICRetryIntegrityTag.h new file mode 100644 index 00000000000..3ac9498f567 --- /dev/null +++ b/iocore/net/quic/QUICRetryIntegrityTag.h @@ -0,0 +1,39 @@ +/** @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" + +class QUICRetryIntegrityTag +{ +public: + static constexpr int LEN = 16; + static bool compute(uint8_t *out, 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}; +}; diff --git a/iocore/net/quic/QUICStream.cc b/iocore/net/quic/QUICStream.cc index 334b9f41bca..93ac8f1f733 100644 --- a/iocore/net/quic/QUICStream.cc +++ b/iocore/net/quic/QUICStream.cc @@ -245,7 +245,7 @@ QUICStreamVConnection::_signal_read_event() } MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread()); - int event = this->_read_vio.ntodo() ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; + int event = this->_read_vio.nbytes == INT64_MAX ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; if (lock.is_locked()) { this->_read_vio.cont->handleEvent(event, &this->_read_vio); diff --git a/iocore/net/quic/QUICStreamManager.cc b/iocore/net/quic/QUICStreamManager.cc index de4ffb9cd18..da66d740822 100644 --- a/iocore/net/quic/QUICStreamManager.cc +++ b/iocore/net/quic/QUICStreamManager.cc @@ -29,10 +29,10 @@ static constexpr char tag[] = "quic_stream_manager"; static constexpr QUICStreamId QUIC_STREAM_TYPES = 4; -QUICStreamManager::QUICStreamManager(QUICConnectionInfoProvider *info, QUICRTTProvider *rtt_provider, QUICApplicationMap *app_map) - : _stream_factory(rtt_provider, info), _info(info), _app_map(app_map) +QUICStreamManager::QUICStreamManager(QUICContext *context, QUICApplicationMap *app_map) + : _stream_factory(context->rtt_provider(), context->connection_info()), _context(context), _app_map(app_map) { - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { this->_next_stream_id_bidi = static_cast(QUICStreamType::CLIENT_BIDI); this->_next_stream_id_uni = static_cast(QUICStreamType::CLIENT_UNI); } else { @@ -265,12 +265,13 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) uint64_t local_max_stream_data = 0; uint64_t remote_max_stream_data = 0; + uint64_t nth_stream = this->_stream_id_to_nth_stream(stream_id); switch (QUICTypeUtil::detect_stream_type(stream_id)) { case QUICStreamType::CLIENT_BIDI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { // client - if (this->_remote_max_streams_bidi == 0 || stream_id > this->_remote_max_streams_bidi) { + if (this->_remote_max_streams_bidi == 0 || nth_stream > this->_remote_max_streams_bidi) { return nullptr; } @@ -278,7 +279,7 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) remote_max_stream_data = this->_remote_tp->getAsUInt(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE); } else { // server - if (this->_local_max_streams_bidi == 0 || stream_id > this->_local_max_streams_bidi) { + if (this->_local_max_streams_bidi == 0 || nth_stream > this->_local_max_streams_bidi) { return nullptr; } @@ -288,14 +289,14 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) break; case QUICStreamType::CLIENT_UNI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { // client - if (this->_remote_max_streams_uni == 0 || stream_id > this->_remote_max_streams_uni) { + if (this->_remote_max_streams_uni == 0 || nth_stream > this->_remote_max_streams_uni) { return nullptr; } } else { // server - if (this->_local_max_streams_uni == 0 || stream_id > this->_local_max_streams_uni) { + if (this->_local_max_streams_uni == 0 || nth_stream > this->_local_max_streams_uni) { return nullptr; } } @@ -305,9 +306,9 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) break; case QUICStreamType::SERVER_BIDI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { // client - if (this->_local_max_streams_bidi == 0 || stream_id > this->_local_max_streams_bidi) { + if (this->_local_max_streams_bidi == 0 || nth_stream > this->_local_max_streams_bidi) { return nullptr; } @@ -315,7 +316,7 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) remote_max_stream_data = this->_remote_tp->getAsUInt(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL); } else { // server - if (this->_remote_max_streams_bidi == 0 || stream_id > this->_remote_max_streams_bidi) { + if (this->_remote_max_streams_bidi == 0 || nth_stream > this->_remote_max_streams_bidi) { return nullptr; } @@ -324,12 +325,12 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) } break; case QUICStreamType::SERVER_UNI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { - if (this->_local_max_streams_uni == 0 || stream_id > this->_local_max_streams_uni) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { + if (this->_local_max_streams_uni == 0 || nth_stream > this->_local_max_streams_uni) { return nullptr; } } else { - if (this->_remote_max_streams_uni == 0 || stream_id > this->_remote_max_streams_uni) { + if (this->_remote_max_streams_uni == 0 || nth_stream > this->_remote_max_streams_uni) { return nullptr; } } @@ -416,6 +417,11 @@ QUICStreamManager::will_generate_frame(QUICEncryptionLevel level, size_t current return false; } + // Don't send DATA frames if current path is not validated + if (!this->_context->path_manager()->get_verified_path().remote_ep().isValid()) { + return false; + } + for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { if (s->will_generate_frame(level, current_packet_size, ack_eliciting, seq_num)) { return true; @@ -466,3 +472,9 @@ QUICStreamManager::_is_level_matched(QUICEncryptionLevel level) return false; } + +uint64_t +QUICStreamManager::_stream_id_to_nth_stream(QUICStreamId stream_id) +{ + return (stream_id / 4) + 1; +} diff --git a/iocore/net/quic/QUICStreamManager.h b/iocore/net/quic/QUICStreamManager.h index fb38ecc1309..05da5fc93ab 100644 --- a/iocore/net/quic/QUICStreamManager.h +++ b/iocore/net/quic/QUICStreamManager.h @@ -31,13 +31,15 @@ #include "QUICFrame.h" #include "QUICStreamFactory.h" #include "QUICLossDetector.h" +#include "QUICPathManager.h" +#include "QUICContext.h" class QUICTransportParameters; class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator { public: - QUICStreamManager(QUICConnectionInfoProvider *info, QUICRTTProvider *rtt_provider, QUICApplicationMap *app_map); + QUICStreamManager(QUICContext *context, QUICApplicationMap *app_map); void init_flow_control_params(const std::shared_ptr &local_tp, const std::shared_ptr &remote_tp); @@ -82,7 +84,7 @@ class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator QUICStreamFactory _stream_factory; - QUICConnectionInfoProvider *_info = nullptr; + QUICContext *_context = nullptr; QUICApplicationMap *_app_map = nullptr; std::shared_ptr _local_tp = nullptr; std::shared_ptr _remote_tp = nullptr; @@ -97,4 +99,6 @@ class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator QUICEncryptionLevel::ZERO_RTT, QUICEncryptionLevel::ONE_RTT, }; + + uint64_t _stream_id_to_nth_stream(QUICStreamId stream_id); }; diff --git a/iocore/net/quic/QUICTLS.cc b/iocore/net/quic/QUICTLS.cc index 5736ef46191..b0317f27a5e 100644 --- a/iocore/net/quic/QUICTLS.cc +++ b/iocore/net/quic/QUICTLS.cc @@ -32,6 +32,55 @@ constexpr static char tag[] = "quic_tls"; +static const char * +content_type_str(int type) +{ + switch (type) { + case SSL3_RT_CHANGE_CIPHER_SPEC: + return "CHANGE_CIPHER_SPEC"; + case SSL3_RT_ALERT: + return "ALERT"; + case SSL3_RT_HANDSHAKE: + return "HANDSHAKE"; + case SSL3_RT_APPLICATION_DATA: + return "APPLICATION_DATA"; + case SSL3_RT_HEADER: + // The buf contains the record header bytes only + return "HEADER"; + default: + return "UNKNOWN"; + } +} + +static const char * +hs_type_str(int type) +{ + switch (type) { + case SSL3_MT_CLIENT_HELLO: + return "CLIENT_HELLO"; + case SSL3_MT_SERVER_HELLO: + return "SERVER_HELLO"; + case SSL3_MT_NEWSESSION_TICKET: + return "NEWSESSION_TICKET"; + case SSL3_MT_END_OF_EARLY_DATA: + return "END_OF_EARLY_DATA"; + case SSL3_MT_ENCRYPTED_EXTENSIONS: + return "ENCRYPTED_EXTENSIONS"; + case SSL3_MT_CERTIFICATE: + return "CERTIFICATE"; + case SSL3_MT_CERTIFICATE_VERIFY: + return "CERTIFICATE_VERIFY"; + case SSL3_MT_FINISHED: + return "FINISHED"; + case SSL3_MT_KEY_UPDATE: + return "KEY_UPDATE"; + case SSL3_MT_MESSAGE_HASH: + return "MESSAGE_HASH"; + default: + return "UNKNOWN"; + } +} + SSL * QUICTLS::ssl_handle() { @@ -50,12 +99,6 @@ QUICTLS::remote_transport_parameters() return this->_remote_transport_parameters; } -void -QUICTLS::set_local_transport_parameters(std::shared_ptr tp) -{ - this->_local_transport_parameters = tp; -} - void QUICTLS::set_remote_transport_parameters(std::shared_ptr tp) { @@ -79,16 +122,30 @@ QUICTLS::~QUICTLS() SSL_free(this->_ssl); } +int +QUICTLS::handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) +{ + if (this->is_handshake_finished()) { + if (in != nullptr && in->offsets[4] != 0) { + return this->_process_post_handshake_messages(*out, in); + } + + return 0; + } + + return this->_handshake(out, in); +} + void QUICTLS::reset() { SSL_clear(this->_ssl); } -uint16_t +uint64_t QUICTLS::convert_to_quic_trans_error_code(uint8_t alert) { - return 0x100 | alert; + return static_cast(QUICTransErrorCode::CRYPTO_ERROR) + alert; } bool @@ -114,7 +171,9 @@ QUICTLS::initialize_key_materials(QUICConnectionId cid) this->_pp_key_info.set_cipher_for_hp_initial(EVP_aes_128_ecb()); // Generate keys - Debug(tag, "Generating %s keys", QUICDebugNames::key_phase(QUICKeyPhase::INITIAL)); + if (is_debug_tag_set(tag)) { + Debug(tag, "Generating %s keys with cid %s", QUICDebugNames::key_phase(QUICKeyPhase::INITIAL), cid.hex().c_str()); + } uint8_t *client_key_for_hp; uint8_t *client_key; @@ -172,6 +231,127 @@ QUICTLS::initialize_key_materials(QUICConnectionId cid) return 1; } +void +QUICTLS::update_negotiated_cipher() +{ + this->_store_negotiated_cipher(); + this->_store_negotiated_cipher_for_hp(); +} + +void +QUICTLS::update_key_materials_for_read(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len) +{ + if (this->_state == HandshakeState::ABORTED) { + return; + } + + QUICKeyPhase phase; + const EVP_CIPHER *cipher; + + switch (level) { + case QUICEncryptionLevel::ZERO_RTT: + phase = QUICKeyPhase::ZERO_RTT; + cipher = this->_pp_key_info.get_cipher(phase); + break; + case QUICEncryptionLevel::HANDSHAKE: + this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); + phase = QUICKeyPhase::HANDSHAKE; + break; + case QUICEncryptionLevel::ONE_RTT: + this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); + // TODO Support Key Update + phase = QUICKeyPhase::PHASE_0; + break; + default: + phase = QUICKeyPhase::INITIAL; + break; + } + + QUICHKDF hkdf(this->_get_handshake_digest()); + + uint8_t *key_for_hp; + uint8_t *key; + uint8_t *iv; + size_t key_for_hp_len; + size_t key_len; + size_t *iv_len; + + cipher = this->_pp_key_info.get_cipher(phase); + key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); + key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); + key = this->_pp_key_info.decryption_key(phase); + key_len = this->_pp_key_info.decryption_key_len(phase); + iv = this->_pp_key_info.decryption_iv(phase); + iv_len = this->_pp_key_info.decryption_iv_len(phase); + + if (this->_netvc_context == NET_VCONNECTION_IN) { + this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - client", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } else { + this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - server", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } + + this->_pp_key_info.set_decryption_key_available(phase); +} + +void +QUICTLS::update_key_materials_for_write(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len) +{ + if (this->_state == HandshakeState::ABORTED) { + return; + } + + QUICKeyPhase phase; + const EVP_CIPHER *cipher = nullptr; + + switch (level) { + case QUICEncryptionLevel::ZERO_RTT: + phase = QUICKeyPhase::ZERO_RTT; + cipher = this->_pp_key_info.get_cipher(phase); + break; + case QUICEncryptionLevel::HANDSHAKE: + this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); + phase = QUICKeyPhase::HANDSHAKE; + cipher = this->_pp_key_info.get_cipher(phase); + break; + case QUICEncryptionLevel::ONE_RTT: + this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); + phase = QUICKeyPhase::PHASE_0; + cipher = this->_pp_key_info.get_cipher(phase); + break; + default: + phase = QUICKeyPhase::INITIAL; + break; + } + + QUICHKDF hkdf(this->_get_handshake_digest()); + + uint8_t *key_for_hp; + uint8_t *key; + uint8_t *iv; + size_t key_for_hp_len; + size_t key_len; + size_t *iv_len; + + key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); + key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); + key = this->_pp_key_info.encryption_key(phase); + key_len = this->_pp_key_info.encryption_key_len(phase); + iv = this->_pp_key_info.encryption_iv(phase); + iv_len = this->_pp_key_info.encryption_iv_len(phase); + + if (this->_netvc_context == NET_VCONNECTION_IN) { + this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - server", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } else { + this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - client", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } + + this->_pp_key_info.set_encryption_key_available(phase); +} + const char * QUICTLS::negotiated_cipher_suite() const { @@ -198,6 +378,118 @@ QUICTLS::abort_handshake() return; } +void +QUICTLS::set_ready_for_write() +{ + this->_should_flush = true; +} + +void +QUICTLS::on_handshake_data_generated(QUICEncryptionLevel level, const uint8_t *data, size_t len) +{ + int index = static_cast(level); + int next_index = index + 1; + + size_t offset = this->_out.offsets[next_index]; + size_t next_level_offset = offset + len; + + memcpy(this->_out.buf + offset, data, len); + + for (int i = next_index; i < 5; ++i) { + this->_out.offsets[i] = next_level_offset; + } +} + +void +QUICTLS::on_tls_alert(uint8_t alert) +{ + this->_has_crypto_error = true; + this->_crypto_error = QUICTLS::convert_to_quic_trans_error_code(alert); +} + +bool +QUICTLS::has_crypto_error() const +{ + return this->_has_crypto_error; +} + +uint64_t +QUICTLS::crypto_error() const +{ + return this->_crypto_error; +} + +int +QUICTLS::_handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) +{ + ink_assert(this->_ssl != nullptr); + if (this->_state == HandshakeState::ABORTED) { + return 0; + } + + int err = SSL_ERROR_NONE; + ERR_clear_error(); + int ret = 0; + + SSL_set_msg_callback(this->_ssl, QUICTLS::_msg_cb); + + this->_out.offsets[0] = 0; + this->_out.offsets[1] = 0; + this->_out.offsets[2] = 0; + this->_out.offsets[3] = 0; + this->_out.offsets[4] = 0; + + if (in) { + this->_pass_quic_data_to_ssl_impl(*in); + } + + if (this->_netvc_context == NET_VCONNECTION_IN) { + if (!this->_early_data_processed) { + if (auto ret = this->_read_early_data(); ret == 0) { + this->_early_data_processed = true; + } else if (ret < 0) { + return 0; + } else { + // Early data is not arrived yet -- can be multiple initial packets + } + } + + ret = SSL_accept(this->_ssl); + } else { + if (!this->_early_data_processed) { + if (this->_write_early_data()) { + this->_early_data_processed = true; + } + } + + ret = SSL_connect(this->_ssl); + } + + if (ret <= 0) { + err = SSL_get_error(this->_ssl, ret); + + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + default: + char err_buf[256] = {0}; + ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); + Debug(tag, "Handshake: %s", err_buf); + return ret; + } + } + + if (this->_should_flush) { + this->_should_flush = false; + *out = &this->_out; + } else { + *out = nullptr; + } + + return 1; +} + void QUICTLS::_update_encryption_level(QUICEncryptionLevel level) { @@ -210,10 +502,10 @@ QUICTLS::_update_encryption_level(QUICEncryptionLevel level) void QUICTLS::_print_km(const char *header, const uint8_t *key_for_hp, size_t key_for_hp_len, const uint8_t *key, size_t key_len, - const uint8_t *iv, size_t iv_len, const uint8_t *secret, size_t secret_len) + const uint8_t *iv, size_t iv_len, const uint8_t *secret, size_t secret_len, QUICKeyPhase phase) { if (is_debug_tag_set("vv_quic_crypto")) { - Debug("vv_quic_crypto", "%s", header); + Debug("vv_quic_crypto", "%s - %s", header, QUICDebugNames::key_phase(phase)); uint8_t print_buf[128]; if (secret) { QUICDebug::to_hex(print_buf, static_cast(secret), secret_len); @@ -227,3 +519,12 @@ QUICTLS::_print_km(const char *header, const uint8_t *key_for_hp, size_t key_for Debug("vv_quic_crypto", "hp=%s", print_buf); } } + +void +QUICTLS::_print_hs_message(int content_type, const void *buf, size_t len) +{ + if ((content_type == SSL3_RT_HANDSHAKE || content_type == SSL3_RT_ALERT)) { + int msg_type = reinterpret_cast(buf)[0]; + Debug(tag, "%s (%d), %s (%d) len=%zu", content_type_str(content_type), content_type, hs_type_str(msg_type), msg_type, len); + } +} diff --git a/iocore/net/quic/QUICTLS.h b/iocore/net/quic/QUICTLS.h index 7bba45aff38..e60d9206c8f 100644 --- a/iocore/net/quic/QUICTLS.h +++ b/iocore/net/quic/QUICTLS.h @@ -36,6 +36,9 @@ #include "I_NetVConnection.h" #include "QUICHandshakeProtocol.h" +// TODO: fix size +static constexpr int MAX_HANDSHAKE_MSG_LEN = 65527; + class QUICTLS : public QUICHandshakeProtocol { public: @@ -50,7 +53,7 @@ class QUICTLS : public QUICHandshakeProtocol }; static QUICEncryptionLevel get_encryption_level(int msg_type); - static uint16_t convert_to_quic_trans_error_code(uint8_t alert); + static uint64_t convert_to_quic_trans_error_code(uint8_t alert); std::shared_ptr local_transport_parameters() override; std::shared_ptr remote_transport_parameters() override; @@ -64,16 +67,25 @@ class QUICTLS : public QUICHandshakeProtocol SSL *ssl_handle(); // QUICHandshakeProtocol - int handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) override; + int handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) override; void reset() override; bool is_handshake_finished() const override; bool is_ready_to_derive() const override; int initialize_key_materials(QUICConnectionId cid) override; - void update_key_materials_on_key_cb(int name, const uint8_t *secret, size_t secret_len); + 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); const char *negotiated_cipher_suite() const override; void negotiated_application_name(const uint8_t **name, unsigned int *len) const override; QUICEncryptionLevel current_encryption_level() const override; void abort_handshake() override; + bool has_crypto_error() const override; + uint64_t crypto_error() const override; + + void set_ready_for_write(); + + void on_handshake_data_generated(QUICEncryptionLevel level, const uint8_t *data, size_t len); + void on_tls_alert(uint8_t alert); private: QUICKeyGenerator _keygen_for_client = QUICKeyGenerator(QUICKeyGenerator::Context::CLIENT); @@ -82,7 +94,8 @@ class QUICTLS : public QUICHandshakeProtocol int _read_early_data(); int _write_early_data(); - int _handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in); + void _pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in); + int _handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in); int _process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in); void _generate_0rtt_key(); void _update_encryption_level(QUICEncryptionLevel level); @@ -91,8 +104,11 @@ class QUICTLS : public QUICHandshakeProtocol void _store_negotiated_cipher_for_hp(); void _print_km(const char *header, const uint8_t *key_for_hp, size_t key_for_hp_len, const uint8_t *key, size_t key_len, - const uint8_t *iv, size_t iv_len, const uint8_t *secret = nullptr, size_t secret_len = 0); + const uint8_t *iv, size_t iv_len, const uint8_t *secret = nullptr, size_t secret_len = 0, + QUICKeyPhase phase = QUICKeyPhase::INITIAL); + static void _print_hs_message(int content_type, const void *buf, size_t len); + static void _msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg); const char *_session_file = nullptr; const char *_keylog_file = nullptr; SSL *_ssl = nullptr; @@ -105,4 +121,10 @@ class QUICTLS : public QUICHandshakeProtocol std::shared_ptr _local_transport_parameters = nullptr; std::shared_ptr _remote_transport_parameters = nullptr; + + uint8_t _out_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; + QUICHandshakeMsgs _out = {_out_buf, MAX_HANDSHAKE_MSG_LEN, {0}, 0}; + bool _should_flush = false; + bool _has_crypto_error = false; + uint64_t _crypto_error = 0; }; diff --git a/iocore/net/quic/QUICTLS_boringssl.cc b/iocore/net/quic/QUICTLS_boringssl.cc index c46b5f256af..46584384801 100644 --- a/iocore/net/quic/QUICTLS_boringssl.cc +++ b/iocore/net/quic/QUICTLS_boringssl.cc @@ -29,7 +29,141 @@ #include #include -// static constexpr char tag[] = "quic_tls"; +#include "QUICGlobals.h" +#include "QUICPacketProtectionKeyInfo.h" + +static constexpr char tag[] = "quic_tls"; + +static QUICEncryptionLevel +convert_level_ats2ssl(enum ssl_encryption_level_t level) +{ + switch (level) { + case ssl_encryption_initial: + return QUICEncryptionLevel::INITIAL; + case ssl_encryption_early_data: + return QUICEncryptionLevel::ZERO_RTT; + case ssl_encryption_handshake: + return QUICEncryptionLevel::HANDSHAKE; + case ssl_encryption_application: + return QUICEncryptionLevel::ONE_RTT; + default: + return QUICEncryptionLevel::NONE; + } +} + +#if BORINGSSL_API_VERSION >= 10 +static int +set_read_secret(SSL *ssl, enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + qtls->update_key_materials_for_read(ats_level, secret, secret_len); + + return 1; +} + +static int +set_write_secret(SSL *ssl, enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + qtls->update_key_materials_for_write(ats_level, secret, secret_len); + + if (ats_level == QUICEncryptionLevel::ONE_RTT) { + // FIXME Where should this be placed? + const uint8_t *tp_buf; + size_t tp_buf_len; + SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + if (SSL_is_server(ssl)) { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } else { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } + } + + return 1; +} +#else +static int +set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *read_secret, const uint8_t *write_secret, + size_t secret_len) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + if (read_secret) { + qtls->update_key_materials_for_read(ats_level, read_secret, secret_len); + } + if (write_secret) { + qtls->update_key_materials_for_write(ats_level, write_secret, secret_len); + } + + if (ats_level == QUICEncryptionLevel::ONE_RTT) { + // FIXME Where should this be placed? + const uint8_t *tp_buf; + size_t tp_buf_len; + SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + if (SSL_is_server(ssl)) { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } else { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } + } + + return 1; +} +#endif + +static int +add_handshake_data(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *data, size_t len) +{ + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_handshake_data_generated(ats_level, data, len); + + return 1; +} + +static int +flush_flight(SSL *ssl) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->set_ready_for_write(); + + return 1; +} + +static int +send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_tls_alert(alert); + return 1; +} + +#if BORINGSSL_API_VERSION >= 10 +static const SSL_QUIC_METHOD quic_method = {set_read_secret, set_write_secret, add_handshake_data, flush_flight, send_alert}; +#else +static const SSL_QUIC_METHOD quic_method = {set_encryption_secrets, add_handshake_data, flush_flight, send_alert}; +#endif + +void +QUICTLS::_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +{ + // Debug for reading + if (write_p == 0) { + QUICTLS::_print_hs_message(content_type, buf, len); + } +} QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, const NetVCOptions &netvc_options, const char *session_file, const char *keylog_file) @@ -40,140 +174,188 @@ QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, Net _netvc_context(nvc_ctx) { ink_assert(this->_netvc_context != NET_VCONNECTION_UNSET); + + if (this->_netvc_context == NET_VCONNECTION_OUT) { + SSL_set_connect_state(this->_ssl); + + SSL_set_alpn_protos(this->_ssl, reinterpret_cast(netvc_options.alpn_protos.data()), + netvc_options.alpn_protos.size()); + const ats_scoped_str &tlsext_host_name = netvc_options.sni_hostname ? netvc_options.sni_hostname : netvc_options.sni_servername; + SSL_set_tlsext_host_name(this->_ssl, tlsext_host_name.get()); + } else { + SSL_set_accept_state(this->_ssl); + } + + SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_tls_index, this); + SSL_set_quic_method(this->_ssl, &quic_method); + SSL_set_early_data_enabled(this->_ssl, 1); + + if (session_file && this->_netvc_context == NET_VCONNECTION_OUT) { + auto file = BIO_new_file(session_file, "r"); + if (file == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + return; + } + + auto session = PEM_read_bio_SSL_SESSION(file, nullptr, nullptr, nullptr); + if (session == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + } else { + if (!SSL_set_session(this->_ssl, session)) { + Debug(tag, "Session resumption failed : %s", session_file); + } else { + Debug(tag, "Session resumption success : %s", session_file); + this->_is_session_reused = true; + } + SSL_SESSION_free(session); + } + + BIO_free(file); + } } -int -QUICTLS::handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) +void +QUICTLS::set_local_transport_parameters(std::shared_ptr tp) { - ink_assert(false); - return 0; + this->_local_transport_parameters = tp; + + uint8_t buf[UINT16_MAX]; + uint16_t len; + this->_local_transport_parameters->store(buf, &len); + SSL_set_quic_transport_params(this->_ssl, buf, len); } int QUICTLS::_process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) { - ink_assert(false); - return 0; + this->_pass_quic_data_to_ssl_impl(*in); + return SSL_process_quic_post_handshake(this->_ssl); } -int -QUICTLS::_read_early_data() +void +QUICTLS::_store_negotiated_cipher() { - uint8_t early_data[8]; - do { - ERR_clear_error(); - SSL_read(this->_ssl, early_data, sizeof(early_data)); - } while (SSL_in_early_data(this->_ssl)); + ink_assert(this->_ssl); - return 1; -} + const EVP_CIPHER *cipher = nullptr; + size_t tag_len = 0; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); -/* -const EVP_AEAD * -QUICTLS::_get_evp_aead(QUICKeyPhase phase) const -{ - if (phase == QUICKeyPhase::INITIAL) { - return EVP_aead_aes_128_gcm(); - } else { - const SSL_CIPHER *cipher = SSL_get_current_cipher(this->_ssl); - if (cipher) { - switch (SSL_CIPHER_get_id(cipher)) { - case TLS1_CK_AES_128_GCM_SHA256: - return EVP_aead_aes_128_gcm(); - case TLS1_CK_AES_256_GCM_SHA384: - return EVP_aead_aes_256_gcm(); - case TLS1_CK_CHACHA20_POLY1305_SHA256: - return EVP_aead_chacha20_poly1305(); - default: - ink_assert(false); - return nullptr; - } - } else { + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_CK_AES_128_GCM_SHA256: + cipher = EVP_aes_128_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_CK_AES_256_GCM_SHA384: + cipher = EVP_aes_256_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_CK_CHACHA20_POLY1305_SHA256: + // cipher = EVP_chacha20_poly1305(); + cipher = nullptr; + tag_len = 16; + break; + default: ink_assert(false); - return nullptr; } + } else { + ink_assert(false); } + + this->_pp_key_info.set_cipher(cipher, tag_len); } -size_t -QUICTLS::_get_aead_tag_len(QUICKeyPhase phase) const +void +QUICTLS::_store_negotiated_cipher_for_hp() { - if (phase == QUICKeyPhase::INITIAL) { - return EVP_GCM_TLS_TAG_LEN; - } else { - const SSL_CIPHER *cipher = SSL_get_current_cipher(this->_ssl); - if (cipher) { - switch (SSL_CIPHER_get_id(cipher)) { - case TLS1_CK_AES_128_GCM_SHA256: - case TLS1_CK_AES_256_GCM_SHA384: - return EVP_GCM_TLS_TAG_LEN; - case TLS1_CK_CHACHA20_POLY1305_SHA256: - return 16; - default: - ink_assert(false); - return -1; - } - } else { + ink_assert(this->_ssl); + + const EVP_CIPHER *cipher_for_hp = nullptr; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_CK_AES_128_GCM_SHA256: + cipher_for_hp = EVP_aes_128_ecb(); + break; + case TLS1_CK_AES_256_GCM_SHA384: + cipher_for_hp = EVP_aes_256_ecb(); + break; + case TLS1_CK_CHACHA20_POLY1305_SHA256: + // cipher_for_hp = EVP_chacha20(); + cipher_for_hp = nullptr; + break; + default: ink_assert(false); - return -1; + break; } + } else { + ink_assert(false); } + + this->_pp_key_info.set_cipher_for_hp(cipher_for_hp); } -const EVP_MD * -QUICKeyGenerator::_get_handshake_digest() +int +QUICTLS::_read_early_data() { - // TODO not implemented - return nullptr; + // This is for Hacked OpenSSL. Do nothing here. + return 1; } -bool -QUICTLS::_encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const uint8_t *plain, size_t plain_len, - uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const KeyMaterial &km, const EVP_AEAD *aead, - size_t tag_len) const +int +QUICTLS::_write_early_data() { - uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; - size_t nonce_len = 0; - _gen_nonce(nonce, nonce_len, pkt_num, km.iv, km.iv_len); - - EVP_AEAD_CTX *aead_ctx = EVP_AEAD_CTX_new(aead, km.key, km.key_len, tag_len); - if (!aead_ctx) { - Debug(tag, "Failed to create EVP_AEAD_CTX"); - return false; - } - - if (!EVP_AEAD_CTX_seal(aead_ctx, cipher, &cipher_len, max_cipher_len, nonce, nonce_len, plain, plain_len, ad, ad_len)) { - Debug(tag, "Failed to encrypt"); - return false; - } - - EVP_AEAD_CTX_free(aead_ctx); - - return true; + // This is for Hacked OpenSSL. Do nothing here. + return 1; } -bool -QUICTLS::_decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, size_t cipher_len, - uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const KeyMaterial &km, const EVP_AEAD *aead, - size_t tag_len) const +void +QUICTLS::_pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in) { - uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; - size_t nonce_len = 0; - _gen_nonce(nonce, nonce_len, pkt_num, km.iv, km.iv_len); - - EVP_AEAD_CTX *aead_ctx = EVP_AEAD_CTX_new(aead, km.key, km.key_len, tag_len); - if (!aead_ctx) { - Debug(tag, "Failed to create EVP_AEAD_CTX"); - return false; + for (auto level : QUIC_ENCRYPTION_LEVELS) { + int index = static_cast(level); + ssl_encryption_level_t ossl_level; + switch (level) { + case QUICEncryptionLevel::INITIAL: + ossl_level = ssl_encryption_initial; + break; + case QUICEncryptionLevel::ZERO_RTT: + ossl_level = ssl_encryption_early_data; + break; + case QUICEncryptionLevel::HANDSHAKE: + ossl_level = ssl_encryption_handshake; + break; + case QUICEncryptionLevel::ONE_RTT: + ossl_level = ssl_encryption_application; + break; + default: + // Should not be happend + ossl_level = ssl_encryption_application; + break; + } + if (in.offsets[index + 1] - in.offsets[index]) { + int start = 0; + for (int i = 0; i < index; ++i) { + start += in.offsets[index]; + } + SSL_provide_quic_data(this->_ssl, ossl_level, in.buf + start, in.offsets[index + 1] - in.offsets[index]); + } } +} - if (!EVP_AEAD_CTX_open(aead_ctx, plain, &plain_len, max_plain_len, nonce, nonce_len, cipher, cipher_len, ad, ad_len)) { - Debug(tag, "Failed to decrypt"); - return false; +const EVP_MD * +QUICTLS::_get_handshake_digest() const +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(this->_ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return EVP_sha256(); + case TLS1_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + ink_assert(false); + return nullptr; } - - EVP_AEAD_CTX_free(aead_ctx); - - return true; } -*/ diff --git a/iocore/net/quic/QUICTLS_legacy.cc b/iocore/net/quic/QUICTLS_legacy.cc new file mode 100644 index 00000000000..3b6e8ea4fca --- /dev/null +++ b/iocore/net/quic/QUICTLS_legacy.cc @@ -0,0 +1,445 @@ +/** @file + * + * QUIC Crypto (TLS to Secure QUIC) using OpenSSL + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "QUICTLS.h" + +#include +#include +#include +#include +#include + +#include "QUICConfig.h" +#include "QUICGlobals.h" +#include "QUICDebugNames.h" +#include "QUICPacketProtectionKeyInfo.h" + +static constexpr char tag[] = "quic_tls"; + +using namespace std::literals; + +static constexpr std::string_view QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_EARLY_TRAFFIC_SECRET"sv); +static constexpr std::string_view QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET"sv); +static constexpr std::string_view QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET"sv); +// TODO: support key update +static constexpr std::string_view QUIC_CLIENT_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_TRAFFIC_SECRET_0"sv); +static constexpr std::string_view QUIC_SERVER_TRAFFIC_SECRET_LABEL("QUIC_SERVER_TRAFFIC_SECRET_0"sv); + +void +QUICTLS::_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +{ + // Debug for reading + if (write_p == 0) { + QUICTLS::_print_hs_message(content_type, buf, len); + return; + } + + if (!write_p || (content_type != SSL3_RT_HANDSHAKE && content_type != SSL3_RT_ALERT)) { + return; + } + + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + const uint8_t *data = reinterpret_cast(buf); + if (content_type == SSL3_RT_HANDSHAKE) { + if (version != TLS1_3_VERSION) { + return; + } + + QUICEncryptionLevel level = QUICTLS::get_encryption_level(data[0]); + qtls->on_handshake_data_generated(level, data, len); + qtls->set_ready_for_write(); + } else if (content_type == SSL3_RT_ALERT && data[0] == SSL3_AL_FATAL && len == 2) { + qtls->on_tls_alert(data[1]); + } + + return; +} + +/** + This is very inspired from writting keylog format of ngtcp2's examples + https://github.com/ngtcp2/ngtcp2/blob/894ed23c970d61eede74f69d9178090af63fdf70/examples/keylog.cc + */ +static void +log_secret(SSL *ssl, int name, const unsigned char *secret, size_t secretlen) +{ + if (auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl))) { + unsigned char crandom[32]; + if (SSL_get_client_random(ssl, crandom, sizeof(crandom)) != sizeof(crandom)) { + return; + } + uint8_t line[256] = {0}; + size_t len = 0; + switch (name) { + case SSL_KEY_CLIENT_EARLY_TRAFFIC: + memcpy(line, QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: + memcpy(line, QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: + memcpy(line, QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: + memcpy(line, QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_SERVER_APPLICATION_TRAFFIC: + memcpy(line, QUIC_SERVER_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_SERVER_TRAFFIC_SECRET_LABEL.size(); + break; + + default: + return; + } + + line[len] = ' '; + ++len; + QUICDebug::to_hex(line + len, crandom, sizeof(crandom)); + len += sizeof(crandom) * 2; + line[len] = ' '; + ++len; + QUICDebug::to_hex(line + len, secret, secretlen); + + keylog_cb(ssl, reinterpret_cast(line)); + } +} + +static int +key_cb(SSL *ssl, int name, const unsigned char *secret, size_t secret_len, void *arg) +{ + if (arg == nullptr) { + return 0; + } + + QUICTLS *qtls = reinterpret_cast(arg); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel level; + switch (name) { + case SSL_KEY_CLIENT_EARLY_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::ZERO_RTT; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_read(level, secret, secret_len); + } else { + qtls->update_key_materials_for_write(level, secret, secret_len); + } + break; + case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::HANDSHAKE; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_read(level, secret, secret_len); + } else { + qtls->update_key_materials_for_write(level, secret, secret_len); + } + break; + case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::HANDSHAKE; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_write(level, secret, secret_len); + } else { + qtls->update_key_materials_for_read(level, secret, secret_len); + } + break; + case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::ONE_RTT; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_read(level, secret, secret_len); + } else { + qtls->update_key_materials_for_write(level, secret, secret_len); + } + break; + case SSL_KEY_SERVER_APPLICATION_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_SERVER_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::ONE_RTT; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_write(level, secret, secret_len); + } else { + qtls->update_key_materials_for_read(level, secret, secret_len); + } + break; + default: + level = QUICEncryptionLevel::NONE; + break; + } + + log_secret(ssl, name, secret, secret_len); + + return 1; +} + +QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, + const NetVCOptions &netvc_options, const char *session_file, const char *keylog_file) + : QUICHandshakeProtocol(pp_key_info), + _session_file(session_file), + _keylog_file(keylog_file), + _ssl(SSL_new(ssl_ctx)), + _netvc_context(nvc_ctx) +{ + ink_assert(this->_netvc_context != NET_VCONNECTION_UNSET); + + if (this->_netvc_context == NET_VCONNECTION_OUT) { + SSL_set_connect_state(this->_ssl); + + SSL_set_alpn_protos(this->_ssl, reinterpret_cast(netvc_options.alpn_protos.data()), + netvc_options.alpn_protos.size()); + const ats_scoped_str &tlsext_host_name = netvc_options.sni_hostname ? netvc_options.sni_hostname : netvc_options.sni_servername; + SSL_set_tlsext_host_name(this->_ssl, tlsext_host_name.get()); + } else { + SSL_set_accept_state(this->_ssl); + } + + SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_tls_index, this); + SSL_set_key_callback(this->_ssl, key_cb, this); + + if (session_file && this->_netvc_context == NET_VCONNECTION_OUT) { + auto file = BIO_new_file(session_file, "r"); + if (file == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + return; + } + + auto session = PEM_read_bio_SSL_SESSION(file, nullptr, nullptr, nullptr); + if (session == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + } else { + if (!SSL_set_session(this->_ssl, session)) { + Debug(tag, "Session resumption failed : %s", session_file); + } else { + Debug(tag, "Session resumption success : %s", session_file); + this->_is_session_reused = true; + } + SSL_SESSION_free(session); + } + + BIO_free(file); + } +} + +QUICEncryptionLevel +QUICTLS::get_encryption_level(int msg_type) +{ + switch (msg_type) { + case SSL3_MT_CLIENT_HELLO: + case SSL3_MT_SERVER_HELLO: + return QUICEncryptionLevel::INITIAL; + case SSL3_MT_END_OF_EARLY_DATA: + return QUICEncryptionLevel::ZERO_RTT; + case SSL3_MT_ENCRYPTED_EXTENSIONS: + case SSL3_MT_CERTIFICATE_REQUEST: + case SSL3_MT_CERTIFICATE: + case SSL3_MT_CERTIFICATE_VERIFY: + case SSL3_MT_FINISHED: + return QUICEncryptionLevel::HANDSHAKE; + case SSL3_MT_KEY_UPDATE: + case SSL3_MT_NEWSESSION_TICKET: + return QUICEncryptionLevel::ONE_RTT; + default: + return QUICEncryptionLevel::NONE; + } +} + +void +QUICTLS::set_local_transport_parameters(std::shared_ptr tp) +{ + this->_local_transport_parameters = tp; +} + +int +QUICTLS::_process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) +{ + ink_assert(this->_ssl != nullptr); + + int err = SSL_ERROR_NONE; + ERR_clear_error(); + int ret = 0; + + SSL_set_msg_callback(this->_ssl, QUICTLS::_msg_cb); + SSL_set_msg_callback_arg(this->_ssl, out); + + this->_pass_quic_data_to_ssl_impl(*in); + + uint8_t data[2048]; + size_t l = 0; + ret = SSL_read_ex(this->_ssl, data, 2048, &l); + + if (ret <= 0) { + err = SSL_get_error(this->_ssl, ret); + + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + default: + char err_buf[256] = {0}; + ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); + Debug(tag, "Handshake: %s", err_buf); + return ret; + } + } + + return 1; +} + +void +QUICTLS::_store_negotiated_cipher() +{ + ink_assert(this->_ssl); + + const QUIC_EVP_CIPHER *cipher = nullptr; + size_t tag_len = 0; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_3_CK_AES_128_GCM_SHA256: + cipher = EVP_aes_128_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_3_CK_AES_256_GCM_SHA384: + cipher = EVP_aes_256_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + cipher = EVP_chacha20_poly1305(); + tag_len = EVP_CHACHAPOLY_TLS_TAG_LEN; + break; + case TLS1_3_CK_AES_128_CCM_SHA256: + cipher = EVP_aes_128_ccm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_3_CK_AES_128_CCM_8_SHA256: + cipher = EVP_aes_128_ccm(); + tag_len = EVP_CCM8_TLS_TAG_LEN; + break; + default: + ink_assert(false); + } + } else { + ink_assert(false); + } + + this->_pp_key_info.set_cipher(cipher, tag_len); +} + +void +QUICTLS::_store_negotiated_cipher_for_hp() +{ + ink_assert(this->_ssl); + + const QUIC_EVP_CIPHER *cipher_for_hp = nullptr; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_3_CK_AES_128_GCM_SHA256: + cipher_for_hp = EVP_aes_128_ecb(); + break; + case TLS1_3_CK_AES_256_GCM_SHA384: + cipher_for_hp = EVP_aes_256_ecb(); + break; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + cipher_for_hp = EVP_chacha20(); + break; + case TLS1_3_CK_AES_128_CCM_SHA256: + case TLS1_3_CK_AES_128_CCM_8_SHA256: + cipher_for_hp = EVP_aes_128_ecb(); + break; + default: + ink_assert(false); + break; + } + } else { + ink_assert(false); + } + + this->_pp_key_info.set_cipher_for_hp(cipher_for_hp); +} + +int +QUICTLS::_read_early_data() +{ + uint8_t early_data[8]; + size_t early_data_len = 0; + + // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving + // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. + int ret = SSL_read_early_data(this->_ssl, early_data, sizeof(early_data), &early_data_len); + // error or reading empty data return 1, otherwise return 0. + if (early_data_len != 0) { + return -1; + } + if (ret == SSL_READ_EARLY_DATA_FINISH) { + return 0; + } else { + return 1; + } +} + +int +QUICTLS::_write_early_data() +{ + size_t early_data_len = 0; + + // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving + // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. + SSL_write_early_data(this->_ssl, "", 0, &early_data_len); + // always return 1 + return 1; +} + +void +QUICTLS::_pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in) +{ + // TODO: set BIO_METHOD which read from QUICHandshakeMsgs directly + BIO *rbio = BIO_new(BIO_s_mem()); + // TODO: set dummy BIO_METHOD which do nothing + BIO *wbio = BIO_new(BIO_s_mem()); + if (in.offsets[4] != 0) { + BIO_write(rbio, in.buf, in.offsets[4]); + } + SSL_set_bio(this->_ssl, rbio, wbio); +} + +const EVP_MD * +QUICTLS::_get_handshake_digest() const +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(this->_ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + case TLS1_3_CK_AES_128_CCM_8_SHA256: + return EVP_sha256(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + ink_assert(false); + return nullptr; + } +} diff --git a/iocore/net/quic/QUICTLS_openssl.cc b/iocore/net/quic/QUICTLS_openssl.cc index 75e9842c7db..6b8668c299d 100644 --- a/iocore/net/quic/QUICTLS_openssl.cc +++ b/iocore/net/quic/QUICTLS_openssl.cc @@ -28,366 +28,96 @@ #include #include -#include "QUICConfig.h" #include "QUICGlobals.h" -#include "QUICDebugNames.h" #include "QUICPacketProtectionKeyInfo.h" static constexpr char tag[] = "quic_tls"; -using namespace std::literals; - -static constexpr std::string_view QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_EARLY_TRAFFIC_SECRET"sv); -static constexpr std::string_view QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET"sv); -static constexpr std::string_view QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET"sv); -// TODO: support key update -static constexpr std::string_view QUIC_CLIENT_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_TRAFFIC_SECRET_0"sv); -static constexpr std::string_view QUIC_SERVER_TRAFFIC_SECRET_LABEL("QUIC_SERVER_TRAFFIC_SECRET_0"sv); - -static const char * -content_type_str(int type) +static QUICEncryptionLevel +convert_level_ats2ssl(enum ssl_encryption_level_t level) { - switch (type) { - case SSL3_RT_CHANGE_CIPHER_SPEC: - return "CHANGE_CIPHER_SPEC"; - case SSL3_RT_ALERT: - return "ALERT"; - case SSL3_RT_HANDSHAKE: - return "HANDSHAKE"; - case SSL3_RT_APPLICATION_DATA: - return "APPLICATION_DATA"; - case SSL3_RT_HEADER: - // The buf contains the record header bytes only - return "HEADER"; - case SSL3_RT_INNER_CONTENT_TYPE: - // Used when an encrypted TLSv1.3 record is sent or received. In encrypted TLSv1.3 records the content type in the record header - // is always SSL3_RT_APPLICATION_DATA. The real content type for the record is contained in an "inner" content type. buf - // contains the encoded "inner" content type byte. - return "INNER_CONTENT_TYPE"; - default: - return "UNKNOWN"; - } -} - -static const char * -hs_type_str(int type) -{ - switch (type) { - case SSL3_MT_CLIENT_HELLO: - return "CLIENT_HELLO"; - case SSL3_MT_SERVER_HELLO: - return "SERVER_HELLO"; - case SSL3_MT_NEWSESSION_TICKET: - return "NEWSESSION_TICKET"; - case SSL3_MT_END_OF_EARLY_DATA: - return "END_OF_EARLY_DATA"; - case SSL3_MT_ENCRYPTED_EXTENSIONS: - return "ENCRYPTED_EXTENSIONS"; - case SSL3_MT_CERTIFICATE: - return "CERTIFICATE"; - case SSL3_MT_CERTIFICATE_VERIFY: - return "CERTIFICATE_VERIFY"; - case SSL3_MT_FINISHED: - return "FINISHED"; - case SSL3_MT_KEY_UPDATE: - return "KEY_UPDATE"; - case SSL3_MT_MESSAGE_HASH: - return "MESSAGE_HASH"; + switch (level) { + case ssl_encryption_initial: + return QUICEncryptionLevel::INITIAL; + case ssl_encryption_early_data: + return QUICEncryptionLevel::ZERO_RTT; + case ssl_encryption_handshake: + return QUICEncryptionLevel::HANDSHAKE; + case ssl_encryption_application: + return QUICEncryptionLevel::ONE_RTT; default: - return "UNKNOWN"; + return QUICEncryptionLevel::NONE; } } -static void -msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +static int +set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *read_secret, const uint8_t *write_secret, + size_t secret_len) { - // Debug for reading - if (write_p == 0 && (content_type == SSL3_RT_HANDSHAKE || content_type == SSL3_RT_ALERT)) { - const uint8_t *tmp = reinterpret_cast(buf); - int msg_type = tmp[0]; + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); - Debug(tag, "%s (%d), %s (%d) len=%zu", content_type_str(content_type), content_type, hs_type_str(msg_type), msg_type, len); - return; - } + qtls->update_negotiated_cipher(); - if (!write_p || !arg || (content_type != SSL3_RT_HANDSHAKE && content_type != SSL3_RT_ALERT)) { - return; + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + if (read_secret) { + qtls->update_key_materials_for_read(ats_level, read_secret, secret_len); } - - QUICHandshakeMsgs *msg = reinterpret_cast(arg); - if (msg == nullptr) { - return; + if (write_secret) { + qtls->update_key_materials_for_write(ats_level, write_secret, secret_len); } - const uint8_t *msg_buf = reinterpret_cast(buf); - - if (content_type == SSL3_RT_HANDSHAKE) { - if (version != TLS1_3_VERSION) { - return; - } - - int msg_type = msg_buf[0]; - - QUICEncryptionLevel level = QUICTLS::get_encryption_level(msg_type); - int index = static_cast(level); - int next_index = index + 1; - - size_t offset = msg->offsets[next_index]; - size_t next_level_offset = offset + len; - - memcpy(msg->buf + offset, buf, len); - - for (int i = next_index; i < 5; ++i) { - msg->offsets[i] = next_level_offset; + if (ats_level == QUICEncryptionLevel::ONE_RTT) { + // FIXME Where should this be placed? + const uint8_t *tp_buf; + size_t tp_buf_len; + SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + if (SSL_is_server(ssl)) { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } else { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); } - } else if (content_type == SSL3_RT_ALERT && msg_buf[0] == SSL3_AL_FATAL && len == 2) { - msg->error_code = QUICTLS::convert_to_quic_trans_error_code(msg_buf[1]); } - return; + return 1; } -/** - This is very inspired from writting keylog format of ngtcp2's examples - https://github.com/ngtcp2/ngtcp2/blob/894ed23c970d61eede74f69d9178090af63fdf70/examples/keylog.cc - */ -static void -log_secret(SSL *ssl, int name, const unsigned char *secret, size_t secretlen) +static int +add_handshake_data(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *data, size_t len) { - if (auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl))) { - unsigned char crandom[32]; - if (SSL_get_client_random(ssl, crandom, sizeof(crandom)) != sizeof(crandom)) { - return; - } - uint8_t line[256] = {0}; - size_t len = 0; - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - memcpy(line, QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - memcpy(line, QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - memcpy(line, QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - memcpy(line, QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - memcpy(line, QUIC_SERVER_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_SERVER_TRAFFIC_SECRET_LABEL.size(); - break; - - default: - return; - } + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); - line[len] = ' '; - ++len; - QUICDebug::to_hex(line + len, crandom, sizeof(crandom)); - len += sizeof(crandom) * 2; - line[len] = ' '; - ++len; - QUICDebug::to_hex(line + len, secret, secretlen); + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_handshake_data_generated(ats_level, data, len); - keylog_cb(ssl, reinterpret_cast(line)); - } + return 1; } static int -key_cb(SSL *ssl, int name, const unsigned char *secret, size_t secret_len, void *arg) +flush_flight(SSL *ssl) { - if (arg == nullptr) { - return 0; - } - - QUICTLS *qtls = reinterpret_cast(arg); - qtls->update_key_materials_on_key_cb(name, secret, secret_len); - - log_secret(ssl, name, secret, secret_len); + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->set_ready_for_write(); return 1; } -void -QUICTLS::update_key_materials_on_key_cb(int name, const uint8_t *secret, size_t secret_len) +static int +send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) { - if (is_debug_tag_set("vv_quic_crypto")) { - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_SERVER_TRAFFIC_SECRET_LABEL.data()); - break; - default: - break; - } - } + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_tls_alert(alert); + return 1; +} - if (this->_state == HandshakeState::ABORTED) { - return; - } +static const SSL_QUIC_METHOD quic_method = {set_encryption_secrets, add_handshake_data, flush_flight, send_alert}; - QUICKeyPhase phase; - const QUIC_EVP_CIPHER *cipher; - QUICHKDF hkdf(this->_get_handshake_digest()); - - this->_store_negotiated_cipher(); - this->_store_negotiated_cipher_for_hp(); - - uint8_t *key_for_hp; - uint8_t *key; - uint8_t *iv; - size_t key_for_hp_len; - size_t key_len; - size_t *iv_len; - - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - // this->_update_encryption_level(QUICEncryptionLevel::ZERO_RTT); - phase = QUICKeyPhase::ZERO_RTT; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } - this->_print_km("update - client - 0rtt", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); - phase = QUICKeyPhase::HANDSHAKE; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } - this->_print_km("update - client - handshake", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); - phase = QUICKeyPhase::PHASE_0; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } - this->_print_km("update - client - 1rtt", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); - phase = QUICKeyPhase::HANDSHAKE; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } - this->_print_km("update - server - handshake", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); - phase = QUICKeyPhase::PHASE_0; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } - this->_print_km("update - server - 1rtt", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - default: - break; +void +QUICTLS::_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +{ + // Debug for reading + if (write_p == 0) { + QUICTLS::_print_hs_message(content_type, buf, len); } - - return; } QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, @@ -412,7 +142,10 @@ QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, Net } SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_tls_index, this); - SSL_set_key_callback(this->_ssl, key_cb, this); + SSL_set_quic_method(this->_ssl, &quic_method); +#ifdef HAVE_SSL_SET_QUIC_EARLY_DATA_ENABLED + SSL_set_quic_early_data_enabled(this->_ssl, 1); +#endif if (session_file && this->_netvc_context == NET_VCONNECTION_OUT) { auto file = BIO_new_file(session_file, "r"); @@ -438,147 +171,22 @@ QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, Net } } -QUICEncryptionLevel -QUICTLS::get_encryption_level(int msg_type) -{ - switch (msg_type) { - case SSL3_MT_CLIENT_HELLO: - case SSL3_MT_SERVER_HELLO: - return QUICEncryptionLevel::INITIAL; - case SSL3_MT_END_OF_EARLY_DATA: - return QUICEncryptionLevel::ZERO_RTT; - case SSL3_MT_ENCRYPTED_EXTENSIONS: - case SSL3_MT_CERTIFICATE_REQUEST: - case SSL3_MT_CERTIFICATE: - case SSL3_MT_CERTIFICATE_VERIFY: - case SSL3_MT_FINISHED: - return QUICEncryptionLevel::HANDSHAKE; - case SSL3_MT_KEY_UPDATE: - case SSL3_MT_NEWSESSION_TICKET: - return QUICEncryptionLevel::ONE_RTT; - default: - return QUICEncryptionLevel::NONE; - } -} - -int -QUICTLS::handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) -{ - if (this->is_handshake_finished()) { - if (in != nullptr && in->offsets[4] != 0) { - return this->_process_post_handshake_messages(out, in); - } - - return 0; - } - - return this->_handshake(out, in); -} - -int -QUICTLS::_handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) +void +QUICTLS::set_local_transport_parameters(std::shared_ptr tp) { - ink_assert(this->_ssl != nullptr); - if (this->_state == HandshakeState::ABORTED) { - return 0; - } - - int err = SSL_ERROR_NONE; - ERR_clear_error(); - int ret = 0; - - SSL_set_msg_callback(this->_ssl, msg_cb); - SSL_set_msg_callback_arg(this->_ssl, out); - - // TODO: set BIO_METHOD which read from QUICHandshakeMsgs directly - BIO *rbio = BIO_new(BIO_s_mem()); - // TODO: set dummy BIO_METHOD which do nothing - BIO *wbio = BIO_new(BIO_s_mem()); - if (in != nullptr && in->offsets[4] != 0) { - BIO_write(rbio, in->buf, in->offsets[4]); - } - SSL_set_bio(this->_ssl, rbio, wbio); - - if (this->_netvc_context == NET_VCONNECTION_IN) { - if (!this->_early_data_processed) { - if (this->_read_early_data() != 1) { - out->error_code = static_cast(QUICTransErrorCode::PROTOCOL_VIOLATION); - return 0; - } else { - this->_early_data_processed = true; - } - } - - ret = SSL_accept(this->_ssl); - } else { - if (!this->_early_data_processed) { - if (this->_write_early_data()) { - this->_early_data_processed = true; - } - } - - ret = SSL_connect(this->_ssl); - } + this->_local_transport_parameters = tp; - if (ret <= 0) { - err = SSL_get_error(this->_ssl, ret); - - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - break; - default: - char err_buf[256] = {0}; - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - Debug(tag, "Handshake: %s", err_buf); - return ret; - } - } - - return 1; + uint8_t buf[UINT16_MAX]; + uint16_t len; + this->_local_transport_parameters->store(buf, &len); + SSL_set_quic_transport_params(this->_ssl, buf, len); } int QUICTLS::_process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) { - ink_assert(this->_ssl != nullptr); - - int err = SSL_ERROR_NONE; - ERR_clear_error(); - int ret = 0; - - SSL_set_msg_callback(this->_ssl, msg_cb); - SSL_set_msg_callback_arg(this->_ssl, out); - - // TODO: set BIO_METHOD which read from QUICHandshakeMsgs directly - BIO *rbio = BIO_new(BIO_s_mem()); - // TODO: set dummy BIO_METHOD which do nothing - BIO *wbio = BIO_new(BIO_s_mem()); - if (in != nullptr && in->offsets[4] != 0) { - BIO_write(rbio, in->buf, in->offsets[4]); - } - SSL_set_bio(this->_ssl, rbio, wbio); - - uint8_t data[2048]; - size_t l = 0; - ret = SSL_read_ex(this->_ssl, data, 2048, &l); - - if (ret <= 0) { - err = SSL_get_error(this->_ssl, ret); - - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - break; - default: - char err_buf[256] = {0}; - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - Debug(tag, "Handshake: %s", err_buf); - return ret; - } - } - - return 1; + this->_pass_quic_data_to_ssl_impl(*in); + return SSL_process_quic_post_handshake(this->_ssl); } void @@ -586,9 +194,9 @@ QUICTLS::_store_negotiated_cipher() { ink_assert(this->_ssl); - const QUIC_EVP_CIPHER *cipher = nullptr; - size_t tag_len = 0; - const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + const EVP_CIPHER *cipher = nullptr; + size_t tag_len = 0; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); if (ssl_cipher) { switch (SSL_CIPHER_get_id(ssl_cipher)) { @@ -627,8 +235,8 @@ QUICTLS::_store_negotiated_cipher_for_hp() { ink_assert(this->_ssl); - const QUIC_EVP_CIPHER *cipher_for_hp = nullptr; - const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + const EVP_CIPHER *cipher_for_hp = nullptr; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); if (ssl_cipher) { switch (SSL_CIPHER_get_id(ssl_cipher)) { @@ -659,28 +267,51 @@ QUICTLS::_store_negotiated_cipher_for_hp() int QUICTLS::_read_early_data() { - uint8_t early_data[8]; - size_t early_data_len = 0; - - // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving - // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. - SSL_read_early_data(this->_ssl, early_data, sizeof(early_data), &early_data_len); - // error or reading empty data return 1, otherwise return 0. - return early_data_len != 0 ? 0 : 1; + // This is for Hacked OpenSSL. Do nothing here. + return 1; } int QUICTLS::_write_early_data() { - size_t early_data_len = 0; - - // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving - // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. - SSL_write_early_data(this->_ssl, "", 0, &early_data_len); - // always return 1 + // This is for Hacked OpenSSL. Do nothing here. return 1; } +void +QUICTLS::_pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in) +{ + for (auto level : QUIC_ENCRYPTION_LEVELS) { + int index = static_cast(level); + ssl_encryption_level_t ossl_level; + switch (level) { + case QUICEncryptionLevel::INITIAL: + ossl_level = ssl_encryption_initial; + break; + case QUICEncryptionLevel::ZERO_RTT: + ossl_level = ssl_encryption_early_data; + break; + case QUICEncryptionLevel::HANDSHAKE: + ossl_level = ssl_encryption_handshake; + break; + case QUICEncryptionLevel::ONE_RTT: + ossl_level = ssl_encryption_application; + break; + default: + // Should not be happend + ossl_level = ssl_encryption_application; + break; + } + if (in.offsets[index + 1] - in.offsets[index]) { + int start = 0; + for (int i = 0; i < index; ++i) { + start += in.offsets[index]; + } + SSL_provide_quic_data(this->_ssl, ossl_level, in.buf + start, in.offsets[index + 1] - in.offsets[index]); + } + } +} + const EVP_MD * QUICTLS::_get_handshake_digest() const { diff --git a/iocore/net/quic/QUICTransportParameters.cc b/iocore/net/quic/QUICTransportParameters.cc index 248cfcb6b9c..cead9700e2d 100644 --- a/iocore/net/quic/QUICTransportParameters.cc +++ b/iocore/net/quic/QUICTransportParameters.cc @@ -27,13 +27,14 @@ #include "QUICIntUtil.h" #include "QUICTransportParameters.h" #include "QUICConnection.h" -#include "QUICHandshake.h" #include "QUICDebugNames.h" #include "QUICTLS.h" #include "QUICTypes.h" 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; @@ -81,19 +82,16 @@ QUICTransportParameters::_load(const uint8_t *buf, size_t len) { bool has_error = false; const uint8_t *p = buf; - - // Read size of parameters field - uint16_t nbytes = (p[0] << 8) + p[1]; - p += 2; + size_t l; + uint64_t param_id; + uint64_t param_len; // Read parameters - const uint8_t *end = p + nbytes; - while (p < end) { + while (len) { // Read ID - uint16_t id = 0; - if (end - p >= 2) { - id = (p[0] << 8) + p[1]; - p += 2; + if (!QUICVariableInt::decode(param_id, l, p, len)) { + len -= l; + p += l; } else { has_error = true; break; @@ -101,25 +99,25 @@ QUICTransportParameters::_load(const uint8_t *buf, size_t len) // Check duplication // An endpoint MUST treat receipt of duplicate transport parameters as a connection error of type TRANSPORT_PARAMETER_ERROR - if (this->_parameters.find(id) != this->_parameters.end()) { + if (this->_parameters.find(param_id) != this->_parameters.end()) { has_error = true; break; } // Read length of value - uint16_t len = 0; - if (end - p >= 2) { - len = (p[0] << 8) + p[1]; - p += 2; + if (!QUICVariableInt::decode(param_len, l, p, len)) { + len -= l; + p += l; } else { has_error = true; break; } // Store parameter - if (end - p >= len) { - this->_parameters.insert(std::make_pair(id, new Value(p, len))); - p += len; + if (len >= param_len) { + this->_parameters.insert(std::make_pair(param_id, new Value(p, param_len))); + len -= param_len; + p += param_len; } else { has_error = true; break; @@ -158,7 +156,7 @@ QUICTransportParameters::_validate_parameters() const if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_STREAMS_UNI)) != this->_parameters.end()) { } - if ((ite = this->_parameters.find(QUICTransportParameterId::IDLE_TIMEOUT)) != this->_parameters.end()) { + if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_IDLE_TIMEOUT)) != this->_parameters.end()) { } if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_PACKET_SIZE)) != this->_parameters.end()) { @@ -250,33 +248,19 @@ void QUICTransportParameters::store(uint8_t *buf, uint16_t *len) const { uint8_t *p = buf; + size_t l; - // Write QUIC versions - this->_store(p, len); - p += *len; - - // Write parameters - // XXX parameters_size will be written later - uint8_t *parameters_size = p; - p += sizeof(uint16_t); - + *len = 0; for (auto &it : this->_parameters) { // TODO Skip non-MUST parameters that have their default values - p[0] = (it.first & 0xff00) >> 8; - p[1] = it.first & 0xff; - p += 2; - p[0] = (it.second->len() & 0xff00) >> 8; - p[1] = it.second->len() & 0xff; - p += 2; + QUICVariableInt::encode(p, TRANSPORT_PARAMETERS_MAXIMUM_SIZE, l, it.first); + p += l; + QUICVariableInt::encode(p, TRANSPORT_PARAMETERS_MAXIMUM_SIZE, l, it.second->len()); + p += l; memcpy(p, it.second->data(), it.second->len()); p += it.second->len(); } - ptrdiff_t n = p - parameters_size - sizeof(uint16_t); - - parameters_size[0] = (n & 0xff00) >> 8; - parameters_size[1] = n & 0xff; - *len = (p - buf); } @@ -298,18 +282,17 @@ QUICTransportParameters::_print() const Debug(tag, "%s (%" PRIu32 "): %s", QUICDebugNames::transport_parameter_id(p.first), static_cast(p.first), hex_str); } else if (QUICTransportParameterId::PREFERRED_ADDRESS == p.first) { QUICPreferredAddress pref_addr(p.second->data(), p.second->len()); - char cid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; char token_hex_str[QUICStatelessResetToken::LEN * 2 + 1]; char ep_ipv4_hex_str[512]; char ep_ipv6_hex_str[512]; - pref_addr.cid().hex(cid_hex_str, sizeof(cid_hex_str)); to_hex_str(token_hex_str, sizeof(token_hex_str), pref_addr.token().buf(), QUICStatelessResetToken::LEN); ats_ip_nptop(pref_addr.endpoint_ipv4(), ep_ipv4_hex_str, sizeof(ep_ipv4_hex_str)); ats_ip_nptop(pref_addr.endpoint_ipv6(), ep_ipv6_hex_str, sizeof(ep_ipv6_hex_str)); Debug(tag, "%s: Endpoint(IPv4)=%s, Endpoint(IPv6)=%s, CID=%s, Token=%s", QUICDebugNames::transport_parameter_id(p.first), - ep_ipv4_hex_str, ep_ipv6_hex_str, cid_hex_str, token_hex_str); + ep_ipv4_hex_str, ep_ipv6_hex_str, pref_addr.cid().hex().c_str(), token_hex_str); } else { - Debug(tag, "%s: (long data)", QUICDebugNames::transport_parameter_id(p.first)); + Debug(tag, "%s (%" PRIu32 "): (%u byte data)", QUICDebugNames::transport_parameter_id(p.first), + static_cast(p.first), p.second->len()); } } } @@ -326,12 +309,6 @@ QUICTransportParametersInClientHello::QUICTransportParametersInClientHello(const } } -void -QUICTransportParametersInClientHello::_store(uint8_t *buf, uint16_t *len) const -{ - *len = 0; -} - std::ptrdiff_t QUICTransportParametersInClientHello::_parameters_offset(const uint8_t *) const { @@ -372,12 +349,6 @@ QUICTransportParametersInEncryptedExtensions::QUICTransportParametersInEncrypted } } -void -QUICTransportParametersInEncryptedExtensions::_store(uint8_t *buf, uint16_t *len) const -{ - *len = 0; -} - void QUICTransportParametersInEncryptedExtensions::add_version(QUICVersion version) { @@ -426,8 +397,6 @@ QUICTransportParametersInEncryptedExtensions::_validate_parameters() const #ifndef OPENSSL_IS_BORINGSSL -static constexpr int TRANSPORT_PARAMETERS_MAXIMUM_SIZE = 65535; - // // QUICTransportParametersHandler // diff --git a/iocore/net/quic/QUICTransportParameters.h b/iocore/net/quic/QUICTransportParameters.h index df46d9d3acd..00af57cca44 100644 --- a/iocore/net/quic/QUICTransportParameters.h +++ b/iocore/net/quic/QUICTransportParameters.h @@ -35,7 +35,7 @@ class QUICTransportParameterId public: enum { ORIGINAL_CONNECTION_ID, - IDLE_TIMEOUT, + MAX_IDLE_TIMEOUT, STATELESS_RESET_TOKEN, MAX_PACKET_SIZE, INITIAL_MAX_DATA, @@ -109,7 +109,6 @@ class QUICTransportParameters virtual std::ptrdiff_t _parameters_offset(const uint8_t *buf) const = 0; virtual int _validate_parameters() const; - virtual void _store(uint8_t *buf, uint16_t *len) const = 0; void _print() const; std::map _parameters; @@ -124,7 +123,6 @@ class QUICTransportParametersInClientHello : public QUICTransportParameters protected: std::ptrdiff_t _parameters_offset(const uint8_t *buf) const override; int _validate_parameters() const override; - void _store(uint8_t *buf, uint16_t *len) const override; private: }; @@ -139,7 +137,6 @@ class QUICTransportParametersInEncryptedExtensions : public QUICTransportParamet protected: std::ptrdiff_t _parameters_offset(const uint8_t *buf) const override; int _validate_parameters() const override; - void _store(uint8_t *buf, uint16_t *len) const override; uint8_t _n_versions = 0; QUICVersion _versions[256] = {}; diff --git a/iocore/net/quic/QUICTypes.cc b/iocore/net/quic/QUICTypes.cc index 5c354a9c68d..80a8379d2b3 100644 --- a/iocore/net/quic/QUICTypes.cc +++ b/iocore/net/quic/QUICTypes.cc @@ -22,6 +22,10 @@ */ #include +#include +#include +#include + #include "QUICTypes.h" #include "QUICIntUtil.h" #include "tscore/CryptoHash.h" @@ -190,15 +194,15 @@ QUICTypeUtil::read_QUICVersion(const uint8_t *buf) } QUICStreamId -QUICTypeUtil::read_QUICStreamId(const uint8_t *buf) +QUICTypeUtil::read_QUICStreamId(const uint8_t *buf, size_t buf_len) { - return static_cast(QUICIntUtil::read_QUICVariableInt(buf)); + return static_cast(QUICIntUtil::read_QUICVariableInt(buf, buf_len)); } QUICOffset -QUICTypeUtil::read_QUICOffset(const uint8_t *buf) +QUICTypeUtil::read_QUICOffset(const uint8_t *buf, size_t buf_len) { - return static_cast(QUICIntUtil::read_QUICVariableInt(buf)); + return static_cast(QUICIntUtil::read_QUICVariableInt(buf, buf_len)); } uint16_t @@ -214,9 +218,9 @@ QUICTypeUtil::read_QUICAppErrorCode(const uint8_t *buf) } uint64_t -QUICTypeUtil::read_QUICMaxData(const uint8_t *buf) +QUICTypeUtil::read_QUICMaxData(const uint8_t *buf, size_t buf_len) { - return QUICIntUtil::read_QUICVariableInt(buf); + return QUICIntUtil::read_QUICVariableInt(buf, buf_len); } void @@ -284,6 +288,27 @@ QUICStatelessResetToken::QUICStatelessResetToken(const QUICConnectionId &conn_id QUICIntUtil::write_uint_as_nbytes(_hash.u64[1], 8, _token + 8, &dummy); } +uint64_t +QUICStatelessResetToken::_hashcode() const +{ + return (static_cast(this->_token[0]) << 56) + (static_cast(this->_token[1]) << 48) + + (static_cast(this->_token[2]) << 40) + (static_cast(this->_token[3]) << 32) + + (static_cast(this->_token[4]) << 24) + (static_cast(this->_token[5]) << 16) + + (static_cast(this->_token[6]) << 8) + (static_cast(this->_token[7])); +} + +std::string +QUICStatelessResetToken::hex() const +{ + std::stringstream stream; + stream << "0x"; + for (auto i = 0; i < QUICStatelessResetToken::LEN; i++) { + stream << std::setfill('0') << std::setw(2) << std::hex; + stream << std::hex << static_cast(this->_token[i]); + } + return stream.str(); +} + QUICResumptionToken::QUICResumptionToken(const IpEndpoint &src, QUICConnectionId cid, ink_hrtime expire_time) { // TODO: read cookie secret from file like SSLTicketKeyConfig @@ -577,7 +602,7 @@ QUICConnectionId QUICConnectionId::ZERO() { uint8_t zero[MAX_LENGTH] = {0}; - return QUICConnectionId(zero, sizeof(zero)); + return QUICConnectionId(zero, 0); } QUICConnectionId::QUICConnectionId() @@ -587,7 +612,8 @@ QUICConnectionId::QUICConnectionId() QUICConnectionId::QUICConnectionId(const uint8_t *buf, uint8_t len) : _len(len) { - memcpy(this->_id, buf, len); + ink_assert(len <= QUICConnectionId::MAX_LENGTH); + memcpy(this->_id, buf, std::min(static_cast(len), QUICConnectionId::MAX_LENGTH)); } uint8_t @@ -636,10 +662,16 @@ QUICConnectionId::h32() const return static_cast(QUICIntUtil::read_nbytes_as_uint(this->_id, 4)); } -int -QUICConnectionId::hex(char *buf, size_t len) const +std::string +QUICConnectionId::hex() const { - return to_hex_str(buf, len, this->_id, this->_len); + std::stringstream stream; + stream << "0x"; + for (auto i = 0; i < this->_len; i++) { + stream << std::setfill('0') << std::setw(2) << std::hex; + stream << std::hex << static_cast(this->_id[i]); + } + return stream.str(); } // @@ -692,8 +724,11 @@ QUICInvariants::scil(uint8_t &dst, const uint8_t *buf, uint64_t buf_len) if (!QUICInvariants::dcil(dcil, buf, buf_len)) { return false; } - - dst = buf[QUICInvariants::LH_CIL_OFFSET + 1 + dcil]; + uint64_t scil_offset = QUICInvariants::LH_CIL_OFFSET + 1 + dcil; + if (scil_offset >= buf_len) { + return false; + } + dst = buf[scil_offset]; return true; } @@ -723,6 +758,10 @@ QUICInvariants::dcid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len dcid_offset = QUICInvariants::SH_DCID_OFFSET; } + if (dcid_len > QUICConnectionId::MAX_LENGTH) { + return false; + } + if (dcid_offset + dcid_len > buf_len) { return false; } @@ -769,3 +808,18 @@ QUICInvariants::scid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len return true; } + +namespace QUICBase +{ +std::string +to_hex(const uint8_t *buf, size_t len) +{ + std::stringstream stream; + stream << "0x"; + for (size_t i = 0; i < len; i++) { + stream << std::setfill('0') << std::setw(2) << std::hex; + stream << std::hex << static_cast(buf[i]); + } + return stream.str(); +} +} // namespace QUICBase diff --git a/iocore/net/quic/QUICTypes.h b/iocore/net/quic/QUICTypes.h index 7c3a53796c7..5139b7821f1 100644 --- a/iocore/net/quic/QUICTypes.h +++ b/iocore/net/quic/QUICTypes.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include "tscore/ink_endian.h" #include "tscore/ink_hrtime.h" #include "tscore/Ptr.h" @@ -34,6 +35,7 @@ #include #include #include +#include #include "tscore/ink_memory.h" #include "tscore/ink_inet.h" #include "openssl/evp.h" @@ -51,7 +53,7 @@ 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[] = { - 0xff000017, + 0xff00001b, }; constexpr QUICVersion QUIC_EXERCISE_VERSION = 0x1a2a3a4a; @@ -115,7 +117,8 @@ enum class QUICFrameType : uint8_t { PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE, // 0x1c - 0x1d - UNKNOWN = 0x1e, + HANDSHAKE_DONE = 0x1e, + UNKNOWN = 0x1f, }; enum class QUICVersionNegotiationStatus { @@ -158,7 +161,9 @@ enum class QUICTransErrorCode : uint64_t { FINAL_SIZE_ERROR, FRAME_ENCODING_ERROR, TRANSPORT_PARAMETER_ERROR, - PROTOCOL_VIOLATION = 0x0A, + CONNECTION_ID_LIMIT_ERROR, + PROTOCOL_VIOLATION, + INVALID_TOKEN, CRYPTO_BUFFER_EXCEEDED = 0x0D, CRYPTO_ERROR = 0x0100, // 0x100 - 0x1FF }; @@ -224,9 +229,9 @@ class QUICConnectionId public: static uint8_t SCID_LEN; - static const int MIN_LENGTH_FOR_INITIAL = 8; - static const int MAX_LENGTH = 20; - static const size_t MAX_HEX_STR_LENGTH = MAX_LENGTH * 2 + 1; + static constexpr int MIN_LENGTH_FOR_INITIAL = 8; + static constexpr int MAX_LENGTH = 20; + static constexpr size_t MAX_HEX_STR_LENGTH = MAX_LENGTH * 2 + 1; static QUICConnectionId ZERO(); QUICConnectionId(); QUICConnectionId(const uint8_t *buf, uint8_t len); @@ -259,7 +264,7 @@ class QUICConnectionId * This is just for debugging. */ uint32_t h32() const; - int hex(char *buf, size_t len) const; + std::string hex() const; uint8_t length() const; bool is_zero() const; @@ -280,22 +285,36 @@ class QUICStatelessResetToken QUICStatelessResetToken(const QUICConnectionId &conn_id, uint32_t instance_id); QUICStatelessResetToken(const uint8_t *buf) { memcpy(this->_token, buf, QUICStatelessResetToken::LEN); } + /** + * Note that this returns a kind of hash code so we can use a StatelessResetToken as a key for a hashtable. + */ + operator uint64_t() const { return this->_hashcode(); } + bool operator==(const QUICStatelessResetToken &x) const { return memcmp(this->_token, x._token, QUICStatelessResetToken::LEN) == 0; } + bool + operator!=(const QUICStatelessResetToken &x) const + { + return memcmp(this->_token, x._token, QUICStatelessResetToken::LEN) != 0; + } + const uint8_t * buf() const { return _token; } + std::string hex() const; + private: uint8_t _token[LEN] = {0}; void _generate(uint64_t data); + uint64_t _hashcode() const; }; class QUICAddressValidationToken @@ -306,6 +325,8 @@ class QUICAddressValidationToken RETRY, }; + // FIXME Check token length + QUICAddressValidationToken(const uint8_t *buf, size_t len) : _token_len(len) { memcpy(this->_token, buf, len); } virtual ~QUICAddressValidationToken(){}; static Type @@ -315,15 +336,30 @@ class QUICAddressValidationToken return static_cast(buf[0]) == Type::RESUMPTION ? Type::RESUMPTION : Type::RETRY; } - virtual const uint8_t *buf() const = 0; - virtual uint8_t length() const = 0; + virtual const uint8_t * + buf() const + { + return this->_token; + } + + virtual uint8_t + length() const + { + return this->_token_len; + } + +protected: + QUICAddressValidationToken() {} + + // The size should be smaller than maximum size of Retry packet + uint8_t _token[1200] = {0}; + unsigned int _token_len; }; class QUICResumptionToken : public QUICAddressValidationToken { public: - QUICResumptionToken() {} - QUICResumptionToken(const uint8_t *buf, uint8_t len) : _token_len(len) { memcpy(this->_token, buf, len); } + QUICResumptionToken(const uint8_t *buf, uint8_t len) : QUICAddressValidationToken(buf, len) {} QUICResumptionToken(const IpEndpoint &src, QUICConnectionId cid, ink_hrtime expire_time); bool @@ -339,29 +375,12 @@ class QUICResumptionToken : public QUICAddressValidationToken const QUICConnectionId cid() const; const ink_hrtime expire_time() const; - - const uint8_t * - buf() const override - { - return this->_token; - } - - uint8_t - length() const override - { - return this->_token_len; - } - -private: - uint8_t _token[1 + EVP_MAX_MD_SIZE + QUICConnectionId::MAX_LENGTH + 4]; - unsigned int _token_len; }; class QUICRetryToken : public QUICAddressValidationToken { public: - QUICRetryToken() {} - QUICRetryToken(const uint8_t *buf, uint8_t len) : _token_len(len) { memcpy(this->_token, buf, len); } + QUICRetryToken(const uint8_t *buf, size_t len) : QUICAddressValidationToken(buf, len) {} QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid); bool @@ -377,21 +396,7 @@ class QUICRetryToken : public QUICAddressValidationToken const QUICConnectionId original_dcid() const; - const uint8_t * - buf() const override - { - return this->_token; - } - - uint8_t - length() const override - { - return this->_token_len; - } - private: - uint8_t _token[1 + EVP_MAX_MD_SIZE + QUICConnectionId::MAX_LENGTH] = {}; - unsigned int _token_len = 0; QUICConnectionId _original_dcid; }; @@ -515,18 +520,20 @@ class QUICPathValidationData class QUICTPConfig { public: - virtual uint32_t no_activity_timeout() const = 0; - virtual const IpEndpoint *preferred_address_ipv4() const = 0; - virtual const IpEndpoint *preferred_address_ipv6() const = 0; - virtual uint32_t initial_max_data() const = 0; - virtual uint32_t initial_max_stream_data_bidi_local() const = 0; - virtual uint32_t initial_max_stream_data_bidi_remote() const = 0; - virtual uint32_t initial_max_stream_data_uni() const = 0; - virtual uint64_t initial_max_streams_bidi() const = 0; - virtual uint64_t initial_max_streams_uni() const = 0; - virtual uint8_t ack_delay_exponent() const = 0; - virtual uint8_t max_ack_delay() const = 0; - virtual uint8_t active_cid_limit() const = 0; + virtual uint32_t no_activity_timeout() const = 0; + virtual const IpEndpoint *preferred_address_ipv4() const = 0; + virtual const IpEndpoint *preferred_address_ipv6() const = 0; + virtual uint32_t initial_max_data() const = 0; + virtual uint32_t initial_max_stream_data_bidi_local() const = 0; + virtual uint32_t initial_max_stream_data_bidi_remote() const = 0; + virtual uint32_t initial_max_stream_data_uni() const = 0; + virtual uint64_t initial_max_streams_bidi() const = 0; + virtual uint64_t initial_max_streams_uni() const = 0; + virtual uint8_t ack_delay_exponent() const = 0; + virtual uint8_t max_ack_delay() const = 0; + virtual uint8_t active_cid_limit() const = 0; + virtual bool disable_active_migration() const = 0; + virtual std::unordered_map> additional_tp() const = 0; }; class QUICLDConfig @@ -566,11 +573,11 @@ class QUICTypeUtil static int read_QUICPacketNumberLen(const uint8_t *buf); static QUICPacketNumber read_QUICPacketNumber(const uint8_t *buf, int len); static QUICVersion read_QUICVersion(const uint8_t *buf); - static QUICStreamId read_QUICStreamId(const uint8_t *buf); - static QUICOffset read_QUICOffset(const uint8_t *buf); + static QUICStreamId read_QUICStreamId(const uint8_t *buf, size_t buf_len); + static QUICOffset read_QUICOffset(const uint8_t *buf, size_t buf_len); static uint16_t read_QUICTransErrorCode(const uint8_t *buf); static QUICAppErrorCode read_QUICAppErrorCode(const uint8_t *buf); - static uint64_t read_QUICMaxData(const uint8_t *buf); + static uint64_t read_QUICMaxData(const uint8_t *buf, size_t buf_len); static void write_QUICConnectionId(QUICConnectionId connection_id, uint8_t *buf, size_t *len); static void write_QUICPacketNumberLen(int len, uint8_t *buf); @@ -605,3 +612,9 @@ class QUICInvariants }; int to_hex_str(char *dst, size_t dst_len, const uint8_t *src, size_t src_len); + +namespace QUICBase +{ +std::string to_hex(const uint8_t *buf, size_t len); + +} // namespace QUICBase diff --git a/iocore/net/quic/QUICVersionNegotiator.cc b/iocore/net/quic/QUICVersionNegotiator.cc index ec1dc7938d6..e08504ce52c 100644 --- a/iocore/net/quic/QUICVersionNegotiator.cc +++ b/iocore/net/quic/QUICVersionNegotiator.cc @@ -35,20 +35,21 @@ QUICVersionNegotiator::negotiate(const QUICPacket &packet) { switch (packet.type()) { case QUICPacketType::INITIAL: { - if (QUICTypeUtil::is_supported_version(packet.version())) { + const QUICInitialPacketR &initial_packet = static_cast(packet); + if (QUICTypeUtil::is_supported_version(initial_packet.version())) { this->_status = QUICVersionNegotiationStatus::NEGOTIATED; - this->_negotiated_version = packet.version(); + this->_negotiated_version = initial_packet.version(); } break; } case QUICPacketType::VERSION_NEGOTIATION: { - const uint8_t *supported_versions = packet.payload(); - uint16_t supported_versions_len = packet.payload_length(); - uint16_t len = 0; + const QUICVersionNegotiationPacketR &vn_packet = static_cast(packet); + uint16_t n_supported_version = vn_packet.nversions(); + uint16_t len = 0; - while (len < supported_versions_len) { - QUICVersion version = QUICTypeUtil::read_QUICVersion(supported_versions + len); + for (int i = 0; i < n_supported_version; ++i) { + QUICVersion version = vn_packet.supported_version(i); len += sizeof(QUICVersion); if (QUICTypeUtil::is_supported_version(version)) { diff --git a/iocore/net/quic/qlog/QLog.cc b/iocore/net/quic/qlog/QLog.cc new file mode 100644 index 00000000000..f94fb398de1 --- /dev/null +++ b/iocore/net/quic/qlog/QLog.cc @@ -0,0 +1,103 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "QLog.h" + +namespace QLog +{ +void +Trace::encode(YAML::Node &node) +{ + node["title"] = _title; + node["description"] = _desc; + + // common fields + { + YAML::Node cf; + cf["ODCID"] = _odcid; + cf["reference_time"] = std::to_string(this->_reference_time / HRTIME_MSECOND); + node["common_fields"] = cf; + } + + { + node["event_fields"].push_back("relative_time"); + node["event_fields"].push_back("category"); + node["event_fields"].push_back("event"); + node["event_fields"].push_back("data"); + + if (_vp.name != "") { + node["vantage_point"]["name"] = _vp.name; + } + + if (vantage_point_type_name(_vp.type)) { + node["vantage_point"]["type"] = vantage_point_type_name(_vp.type); + } + + if (vantage_point_type_name(_vp.flow)) { + node["vantage_point"]["flow"] = vantage_point_type_name(_vp.flow); + } + } + + // events + for (auto &&it : _events) { + YAML::Node sub(YAML::NodeType::value::Sequence); + sub.push_back((it->get_time() - this->_reference_time) / HRTIME_MSECOND); + sub.push_back(it->category()); + sub.push_back(it->event()); + YAML::Node event; + it->encode(event); + sub.push_back(event); + node["events"].push_back(sub); + } +} + +void +QLog::dump(std::string dir) +{ + YAML::Node root; + root["qlog_version"] = this->_ver; + root["title"] = this->_title; + root["description"] = this->_desc; + for (auto &&it : this->_traces) { + YAML::Node node; + it->encode(node); + root["traces"].push_back(node); + } + + auto file = dir; + if (file.back() != '/') { + file += "/"; + } + file += this->last_trace().odcid() + ".qlog"; + std::ofstream ofs; + ofs.open(file, std::ofstream::in | std::ofstream::trunc); + + YAML::Emitter emitter(ofs); + emitter << YAML::DoubleQuoted << YAML::Flow << root; + ofs << "\n"; + ofs.close(); +} + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLog.h b/iocore/net/quic/qlog/QLog.h new file mode 100644 index 00000000000..2407d58202d --- /dev/null +++ b/iocore/net/quic/qlog/QLog.h @@ -0,0 +1,145 @@ +/** @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 +#include + +#include "QLogEvent.h" + +namespace QLog +{ +class Trace +{ +public: + enum class VantagePointType : uint8_t { + client, + server, + network, + unknown, + }; + + struct VantagePoint { + std::string name; + VantagePointType type; + VantagePointType flow = VantagePointType::unknown; + }; + + Trace(std::string odcid, std::string title = "", std::string desc = "") : _reference_time(Thread::get_hrtime()), _odcid(odcid) {} + + Trace(const VantagePoint &vp, std::string odcid, std::string title = "", std::string desc = "") : Trace(odcid, title, desc) + { + set_vantage_point(vp); + } + + static const char * + vantage_point_type_name(VantagePointType ty) + { + switch (ty) { + case VantagePointType::client: + return "client"; + case VantagePointType::server: + return "server"; + case VantagePointType::network: + return "network"; + case VantagePointType::unknown: + return "unknown"; + default: + return nullptr; + } + } + + void + set_vantage_point(const VantagePoint vp) + { + this->_vp = vp; + } + + Trace & + push_event(QLogEventUPtr e) + { + this->_events.push_back(std::move(e)); + return *this; + } + + std::string + odcid() const + { + return this->_odcid; + } + + void encode(YAML::Node &node); + +private: + int64_t _reference_time = Thread::get_hrtime(); + std::string _odcid; + std::string _title; + std::string _desc; + + VantagePoint _vp; + + std::vector _events; +}; + +class QLog +{ +public: + static constexpr char QLOG_VERSION[] = "draft-01"; + + QLog(std::string title = "", std::string desc = "", std::string ver = QLOG_VERSION) : _title(title), _desc(desc), _ver(ver) {} + + Trace & + new_trace(Trace::VantagePoint vp, std::string odcid, std::string title = "", std::string desc = "") + { + this->_traces.push_back(std::make_unique(vp, odcid, title, desc)); + return *this->_traces.back().get(); + } + + Trace & + new_trace(std::string odcid, std::string title = "", std::string desc = "") + { + this->_traces.push_back(std::make_unique(odcid, title, desc)); + return *this->_traces.back().get(); + } + + Trace & + last_trace() + { + if (this->_traces.empty()) { + throw std::invalid_argument("traces is empty"); + } + + return *this->_traces.back().get(); + } + + void dump(std::string dir); + +private: + std::string _title; + std::string _desc; + std::string _ver; + std::vector> _traces; +}; + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogEvent.cc b/iocore/net/quic/qlog/QLogEvent.cc new file mode 100644 index 00000000000..98bec4ed898 --- /dev/null +++ b/iocore/net/quic/qlog/QLogEvent.cc @@ -0,0 +1,317 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QLogEvent.h" + +namespace QLog +{ +void +check_and_set(YAML::Node &node, std::string key, std::string val) +{ + if (val.length() > 0) { + node[key] = val; + } +} + +void +check_and_set(YAML::Node &node, std::string key, std::vector val) +{ + if (val.size() > 0) { + node[key] = val; + } +} + +template +void +check_and_set(YAML::Node &node, std::string key, T val) +{ + if (val) { + node[key] = val; + } +} + +namespace Connectivity +{ + void + ServerListening::encode(YAML::Node &node) + { + check_and_set(node, "ip_v4", _ip_v4); + check_and_set(node, "ip_v6", _ip_v6); + check_and_set(node, "port_v4", _port_v4); + check_and_set(node, "port_v6", _port_v6); + check_and_set(node, "stateless_reset_required", _port_v6); + check_and_set(node, "quic_version", _quic_version); + check_and_set(node, "alpn_values", _alpn_values); + } + + void + ConnectionStarted::encode(YAML::Node &node) + { + check_and_set(node, "quic_version", _quic_version); + check_and_set(node, "ip_version", _ip_version); + check_and_set(node, "src_ip", _src_ip); + check_and_set(node, "dst_ip", _dst_ip); + check_and_set(node, "protocol", _protocol); + check_and_set(node, "src_port", _src_port); + check_and_set(node, "dst_port", _dst_port); + check_and_set(node, "src_cid", _src_cid); + check_and_set(node, "dst_cid", _dst_cid); + check_and_set(node, "alpn_values", _alpn_values); + } + + void + ConnectionIdUpdated::encode(YAML::Node &node) + { + check_and_set(node, "src_old", _src_old); + check_and_set(node, "src_new", _src_new); + check_and_set(node, "dst_old", _dst_old); + check_and_set(node, "dst_new", _dst_new); + } + + void + SpinBitUpdated::encode(YAML::Node &node) + { + check_and_set(node, "state", _state); + } + + void + ConnectionStateUpdated::encode(YAML::Node &node) + { + check_and_set(node, "new", static_cast(_new)); + check_and_set(node, "old", static_cast(_old)); + check_and_set(node, "trigger", trigger_name(_trigger)); + } + +} // namespace Connectivity + +namespace Security +{ + void + KeyEvent::encode(YAML::Node &node) + { + node["key_type"] = static_cast(_key_type); + node["new"] = _new; + check_and_set(node, "generation", _generation); + check_and_set(node, "old", _old); + check_and_set(node, "trigger", trigger_name(_trigger)); + } + +} // namespace Security + +namespace Transport +{ + void + ParametersSet::encode(YAML::Node &node) + { + node["owner"] = _owner ? "local" : "remote"; + check_and_set(node, "resumption_allowed", _resumption_allowed); + check_and_set(node, "early_data_enabled", _early_data_enabled); + check_and_set(node, "alpn", _alpn); + check_and_set(node, "version", _version); + check_and_set(node, "tls_cipher", _tls_cipher); + check_and_set(node, "original_connection_id", _original_connection_id); + check_and_set(node, "stateless_reset_token", _stateless_reset_token); + check_and_set(node, "disable_active_migration", _disable_active_migration); + check_and_set(node, "max_idle_timeout", _max_idle_timeout); + check_and_set(node, "max_udp_payload_size", _max_udp_payload_size); + check_and_set(node, "ack_delay_exponent", _ack_delay_exponent); + check_and_set(node, "max_ack_delay", _max_ack_delay); + check_and_set(node, "active_connection_id_limit", _active_connection_id_limit); + check_and_set(node, "initial_max_data", _initial_max_data); + check_and_set(node, "initial_max_stream_data_bidi_local", _initial_max_stream_data_bidi_local); + check_and_set(node, "initial_max_stream_data_bidi_remote", _initial_max_stream_data_bidi_remote); + check_and_set(node, "initial_max_stream_data_uni", _initial_max_stream_data_uni); + check_and_set(node, "initial_max_streams_bidi", _initial_max_streams_bidi); + check_and_set(node, "initial_max_streams_uni", _initial_max_streams_uni); + + if (_preferred_address.ip.length() > 0) { + YAML::Node sub; + check_and_set(sub, _preferred_address.ipv4 ? "ip_v4" : "ip_v6", _preferred_address.ip); + check_and_set(sub, _preferred_address.ipv4 ? "port_v4" : "port_v6", _preferred_address.port); + check_and_set(sub, "connection_id", _preferred_address.connection_id); + check_and_set(sub, "stateless_reset_token", _preferred_address.stateless_reset_token); + node["preferred_address"] = sub; + } + } + + void + PacketEvent::encode(YAML::Node &node) + { + node["packet_type"] = _packet_type; + for (auto &&it : this->_frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + check_and_set(node, "is_coalesced", _is_coalesced); + check_and_set(node, "stateless_reset_token", _stateless_reset_token); + check_and_set(node, "supported_version", _supported_version); + check_and_set(node, "raw_encrypted", _raw_encrypted); + check_and_set(node, "raw_decrypted", _raw_decrypted); + check_and_set(node, "supported_version", _supported_version); + check_and_set(node, "supported_version", trigger_name(_trigger)); + + node["header"]["packet_number"] = _header.packet_number; + node["header"]["packet_size"] = _header.packet_size; + node["header"]["payload_length"] = _header.payload_length; + node["header"]["version"] = _header.version; + node["header"]["scil"] = _header.scil; + node["header"]["dcil"] = _header.dcil; + node["header"]["scid"] = _header.scid; + node["header"]["dcid"] = _header.dcid; + } + + void + PacketDropped::encode(YAML::Node &node) + { + node["packet_type"] = _packet_type; + check_and_set(node, "packet_size", _packet_size); + check_and_set(node, "raw", _raw); + check_and_set(node, "trigger", trigger_name(_trigger)); + } + + void + PacketBuffered::encode(YAML::Node &node) + { + node["packet_type"] = _packet_type; + check_and_set(node, "trigger", trigger_name(_trigger)); + check_and_set(node, "packet_number", trigger_name(_trigger)); + } + + void + DatagramsEvent::encode(YAML::Node &node) + { + check_and_set(node, "count", _count); + check_and_set(node, "byte_length", _byte_length); + } + + void + DatagramsDropped::encode(YAML::Node &node) + { + check_and_set(node, "byte_length", _byte_length); + } + + void + StreamStateUpdated::encode(YAML::Node &node) + { + node["new"] = static_cast(_new); + node["stream_id"] = _stream_id; + // FXIME + // node["stream_type"] = bidi ? "bidirectional" : "unidirectional"; + // node["stream_side"] = "sending"; + } + + void + FrameProcessed::encode(YAML::Node &node) + { + for (auto &&it : _frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + } + +} // namespace Transport + +namespace Recovery +{ + void + ParametersSet::encode(YAML::Node &node) + { + check_and_set(node, "reordering_threshold", _reordering_threshold); + check_and_set(node, "time_threshold", _time_threshold); + check_and_set(node, "timer_granularity", _timer_granularity); + check_and_set(node, "initial_rtt", _initial_rtt); + check_and_set(node, "max_datagram_size", _max_datagram_size); + check_and_set(node, "initial_congestion_window", _initial_congestion_window); + check_and_set(node, "minimum_congestion_window", _minimum_congestion_window); + check_and_set(node, "loss_reduction_factor", _loss_reduction_factor); + check_and_set(node, "persistent_congestion_threshold", _persistent_congestion_threshold); + } + + void + MetricsUpdated::encode(YAML::Node &node) + { + check_and_set(node, "min_rtt", _min_rtt); + check_and_set(node, "smoothed_rtt", _smoothed_rtt); + check_and_set(node, "latest_rtt", _latest_rtt); + check_and_set(node, "rtt_variance", _rtt_variance); + check_and_set(node, "max_ack_delay", _max_ack_delay); + check_and_set(node, "pto_count", _pto_count); + check_and_set(node, "congestion_window", _congestion_window); + check_and_set(node, "bytes_in_flight", _bytes_in_flight); + check_and_set(node, "ssthresh", _ssthresh); + check_and_set(node, "packets_in_flight", _packets_in_flight); + check_and_set(node, "in_recovery", _in_recovery); + check_and_set(node, "pacing_rate", _pacing_rate); + } + + void + CongestionStateUpdated::encode(YAML::Node &node) + { + node["new"] = state_to_string(_new); + check_and_set(node, "old", state_to_string(_old)); + check_and_set(node, "old", trigger_name(_trigger)); + } + + void + LossTimerUpdated::encode(YAML::Node &node) + { + node["timer_type"] = _timer_type_ack ? "ack" : "pto"; + check_and_set(node, "event_type", event_type_name(_event_type)); + check_and_set(node, "packet_number_space", _packet_number_space); + if (_event_type == EventType::set) { + check_and_set(node, "delta", _delta); + } + } + + void + PacketLost::encode(YAML::Node &node) + { + node["packet_number"] = _packet_number; + node["packet_type"] = _packet_type; + check_and_set(node, "trigger", trigger_name(_trigger)); + YAML::Node sub; + _header.encode(sub); + node["header"] = sub; + + for (auto &&it : _frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + } + + void + MarkedForRetransmit::encode(YAML::Node &node) + { + for (auto &&it : _frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + } + +} // namespace Recovery + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogEvent.h b/iocore/net/quic/qlog/QLogEvent.h new file mode 100644 index 00000000000..a03f7cc4f32 --- /dev/null +++ b/iocore/net/quic/qlog/QLogEvent.h @@ -0,0 +1,1013 @@ +/** @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 +#include + +#include "QUICTypes.h" +#include "QLogEvent.h" +#include "QLogFrame.h" + +namespace QLog +{ +class QLogEvent +{ +public: + virtual ~QLogEvent() {} + + virtual std::string category() const = 0; + virtual std::string event() const = 0; + virtual void encode(YAML::Node &) = 0; + + virtual ink_hrtime + get_time() const + { + return this->_time; + }; + +protected: + ink_hrtime _time = Thread::get_hrtime(); +}; + +using QLogEventUPtr = std::unique_ptr; + +#define SET(field, type) \ + void set_##field(type v) { this->_node[#field] = v; } + +// enum class PacketType : uint8_t { initial, handshake, zerortt, onertt, retry, version_negotiation, unknown }; +using PacketType = std::string; + +struct PacketHeader { + std::string packet_number; + uint64_t packet_size; + uint64_t payload_length; + + // only if present in the header + // if correctly using NEW_CONNECTION_ID events, + // dcid can be skipped for 1RTT packets + std::string version; + std::string scil; + std::string dcil; + std::string scid; + std::string dcid; + + // Note: short vs long header is implicit through PacketType + void + encode(YAML::Node &node) + { + node["packet_number"] = packet_number; + node["packet_size"] = packet_size; + node["payload_length"] = payload_length; + node["version"] = version; + node["scil"] = scil; + node["dcil"] = dcil; + node["scid"] = scid; + node["dcid"] = dcid; + } +}; + +#define SET_FUNC(cla, field, type) \ +public: \ + cla &set_##field(type v) \ + { \ + this->_##field = v; \ + return *this; \ + } \ + \ +private: \ + type _##field; + +#define APPEND_FUNC(cla, field, type) \ +public: \ + cla &append_##field(type v) \ + { \ + this->_##field.push_back(v); \ + return *this; \ + } \ + \ +private: \ + std::vector _##field; + +#define APPEND_FRAME_FUNC(cla) \ +public: \ + cla &append_frames(QLogFrameUPtr v) \ + { \ + this->_frames.push_back(std::move(v)); \ + return *this; \ + } \ + \ +private: \ + std::vector _frames; + +// +// connectivity +// +namespace Connectivity +{ + class ConnectivityEvent : public QLogEvent + { + public: + std::string + category() const override + { + return "connectivity"; + } + }; + + class ServerListening : public ConnectivityEvent + { + public: + ServerListening(int port, bool v6 = false) + { + if (v6) { + set_port_v6(port); + } else { + set_port_v4(port); + } + } + +#define _SET(a, b) SET_FUNC(ServerListening, a, b) +#define _APPEND(a, b) APPEND_FUNC(ServerListening, a, b) + _SET(port_v4, int) + _SET(port_v6, int) + _SET(ip_v4, std::string) + _SET(ip_v6, std::string) + _SET(stateless_reset_required, bool) + _APPEND(quic_version, std::string) + _APPEND(alpn_values, std::string) + +#undef _SET +#undef _APPEND + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "server_listening"; + } + }; + + class ConnectionStarted : public ConnectivityEvent + { + public: + ConnectionStarted(std::string version, std::string sip, std::string dip, int sport, int dport, std::string protocol = "QUIC") + { + set_ip_version(version); + set_protocol(protocol); + set_src_ip(sip); + set_dst_ip(dip); + set_src_port(sport); + set_dst_port(dport); + } + +#define _SET(a, b) SET_FUNC(ConnectionStarted, a, b) +#define _APPEND(a, b) APPEND_FUNC(ConnectionStarted, a, b) + _SET(quic_version, std::string); + _SET(src_cid, std::string); + _SET(dst_cid, std::string); + _SET(protocol, std::string); + _SET(ip_version, std::string) + _SET(src_ip, std::string) + _SET(dst_ip, std::string) + _SET(src_port, int) + _SET(dst_port, int) + _APPEND(alpn_values, std::string) + +#undef _SET +#undef _APPEND + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "connection_started"; + } + }; + + class ConnectionIdUpdated : public ConnectivityEvent + { + public: + ConnectionIdUpdated(std::string old, std::string n, bool peer = false) + { + if (peer) { + set_dst_old(old); + set_dst_new(n); + } else { + set_src_old(old); + set_src_new(n); + } + } + +#define _SET(a, b) SET_FUNC(ConnectionIdUpdated, a, b) +#define _APPEND(a, b) APPEND_FUNC(ConnectionIdUpdated, a, b) + + _SET(src_old, std::string); + _SET(src_new, std::string); + _SET(dst_old, std::string); + _SET(dst_new, std::string); + +#undef _SET +#undef _APPEND + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "connection_id_updated"; + } + }; + + class SpinBitUpdated : public ConnectivityEvent + { + public: + SpinBitUpdated(bool state) { set_state(state); } + +#define _SET(a, b) SET_FUNC(SpinBitUpdated, a, b) + _SET(state, bool); +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "spin_bit_updated"; + } + }; + + class ConnectionStateUpdated : public ConnectivityEvent + { + public: + enum class ConnectionState : uint8_t { + attempted, // client initial sent + reset, // stateless reset sent + handshake, // handshake in progress + active, // handshake successful, data exchange + keepalive, // no data for a longer period + draining, // CONNECTION_CLOSE sent + closed // connection actually fully closed, memory freed + }; + + enum class Triggered : uint8_t { + unknown, + error, // when closing because of an unexpected event + clean, // when closing normally + application // e.g., HTTP/3's GOAWAY frame + }; + + ConnectionStateUpdated(ConnectionState n, Triggered tr = Triggered::unknown) + { + set_new(n); + set_trigger(tr); + } + +#define _SET(a, b) SET_FUNC(ConnectionStateUpdated, a, b) + _SET(new, ConnectionState); + _SET(old, ConnectionState); + _SET(trigger, Triggered) + +#undef _SET + + void encode(YAML::Node &) override; + + static const char * + trigger_name(Triggered trigger) + { + switch (trigger) { + case Triggered::error: + return "error"; + case Triggered::clean: + return "clean"; + case Triggered::application: + return "application"; + default: + return nullptr; + } + } + + std::string + event() const override + { + return "connection_state_updated"; + } + }; + +} // namespace Connectivity + +namespace Security +{ + class KeyEvent : public QLogEvent + { + public: + enum class KeyType : uint8_t { + server_initial_secret, + client_initial_secret, + + server_handshake_secret, + client_handshake_secret, + + server_0rtt_secret, + client_0rtt_secret, + + server_1rtt_secret, + client_1rtt_secret + }; + + enum class Triggered : uint8_t { + unknown, + remote_update, + local_update, + tls, + }; + + KeyEvent(KeyType ty, std::string n, int generation, Triggered triggered = Triggered::unknown) + { + set_key_type(ty); + set_new(n); + set_generation(generation); + set_trigger(triggered); + } + +#define _SET(a, b) SET_FUNC(KeyEvent, a, b) + _SET(key_type, KeyType); + _SET(new, std::string) + _SET(old, std::string); + _SET(generation, int) + _SET(trigger, Triggered) +#undef _SET + + void encode(YAML::Node &) override; + + const char * + trigger_name(Triggered triggered) + { + switch (triggered) { + case Triggered::remote_update: + return "remote_update"; + case Triggered::local_update: + return "local_update"; + case Triggered::tls: + return "tls"; + default: + return nullptr; + } + } + + std::string + category() const override + { + return "security"; + } + }; + + class KeyUpdated : public KeyEvent + { + public: + KeyUpdated(KeyType ty, std::string n, int generation, Triggered triggered = KeyEvent::Triggered::unknown) + : KeyEvent(ty, n, generation, triggered) + { + } + + std::string + event() const override + { + return "key_updated"; + } + }; + + class KeyRetired : public KeyEvent + { + public: + KeyRetired(KeyType ty, std::string n, int generation, Triggered triggered = KeyEvent::Triggered::unknown) + : KeyEvent(ty, n, generation, triggered) + { + } + + std::string + event() const override + { + return "key_retired"; + } + }; + +} // namespace Security + +// +// transport event +// +namespace Transport +{ + class TransportEvent : public QLogEvent + { + public: + std::string + category() const override + { + return "transport"; + } + }; + + class ParametersSet : public TransportEvent + { + public: + struct PreferredAddress { + std::string ip; + int port; + std::string connection_id; + std::string stateless_reset_token; + bool ipv4 = true; + }; + + ParametersSet(bool owner) : _owner(owner) {} + + std::string + event() const override + { + return "parameters_set"; + } + +#define _SET(a, b) SET_FUNC(ParametersSet, a, b) + _SET(resumption_allowed, bool); // early data extension was enabled on the TLS layer + _SET(early_data_enabled, bool); // early data extension was enabled on the TLS layer + _SET(alpn, std::string); + _SET(version, std::string); // hex (e.g. 0x); + _SET(tls_cipher, std::string); // (e.g. AES_128_GCM_SHA256); + _SET(original_connection_id, std::string); // hex + _SET(stateless_reset_token, std::string); // hex + _SET(disable_active_migration, bool); + _SET(idle_timeout, int); + _SET(max_packet_size, int); + _SET(ack_delay_exponent, int); + _SET(max_ack_delay, int); + _SET(active_connection_id_limit, int); + _SET(initial_max_data, std::string); + _SET(initial_max_stream_data_bidi_local, std::string); + _SET(initial_max_stream_data_bidi_remote, std::string); + _SET(initial_max_stream_data_uni, std::string); + _SET(initial_max_streams_bidi, std::string); + _SET(initial_max_streams_uni, std::string); + _SET(max_idle_timeout, int64_t) + _SET(max_udp_payload_size, size_t) + _SET(preferred_address, PreferredAddress) +#undef _SET + + void encode(YAML::Node &) override; + + private: + bool _owner = false; + }; + + class PacketEvent : public TransportEvent + { + public: + enum class Triggered : uint8_t { + unknown, + keys_available, // if packet was buffered because it couldn't be decrypted before + retransmit_reordered, // draft-23 5.1.1 + retransmit_timeout, // draft-23 5.1.2 + pto_probe, // draft-23 5.3.1 + retransmit_crypto, // draft-19 6.2 + cc_bandwidth_probe, // needed for some CCs to figure out bandwidth allocations when there are no normal sends + }; + + PacketEvent(PacketType type, PacketHeader h, Triggered tr = Triggered::unknown) + { + set_packet_type(type).set_header(h).set_trigger(tr); + } + +#define _SET(a, b) SET_FUNC(PacketEvent, a, b) +#define _APPEND(a, b) APPEND_FUNC(PacketEvent, a, b) + _SET(packet_type, PacketType) + _SET(header, PacketHeader) + _SET(is_coalesced, bool); + _SET(raw_encrypted, std::string); + _SET(raw_decrypted, std::string); + _SET(stateless_reset_token, std::string); + _SET(trigger, Triggered); + _APPEND(supported_version, std::string); + +#undef _SET +#undef _APPEND + APPEND_FRAME_FUNC(PacketEvent) + + void encode(YAML::Node &) override; + + static const char * + trigger_name(Triggered triggered) + { + switch (triggered) { + case Triggered::retransmit_reordered: + return "retransmit_reordered"; + case Triggered::retransmit_timeout: + return "retransmit_timeout"; + case Triggered::pto_probe: + return "pto_probe"; + case Triggered::retransmit_crypto: + return "retransmit_crypto"; + case Triggered::cc_bandwidth_probe: + return "cc_bandwidth_probe"; + break; + case Triggered::keys_available: + return "keys_available"; + default: + return nullptr; + } + } + }; + + class PacketSent : public PacketEvent + { + public: + PacketSent(PacketType type, PacketHeader h, Triggered tr = Triggered::unknown) : PacketEvent(type, h, tr) {} + std::string + event() const override + { + return "packet_sent"; + } + }; + + class PacketReceived : public PacketEvent + { + public: + PacketReceived(PacketType type, PacketHeader h, Triggered tr = Triggered::unknown) : PacketEvent(type, h, tr) {} + std::string + event() const override + { + return "packet_received"; + } + }; + + class PacketDropped : public TransportEvent + { + public: + enum class Triggered : uint8_t { + unknown, + key_unavailable, + unknown_connection_id, + header_decrypt_error, + payload_decrypt_error, + protocol_violation, + dos_prevention, + unsupported_version, + unexpected_packet, + unexpected_source_connection_id, + unexpected_version, + }; + + PacketDropped(Triggered tr = Triggered::unknown) { set_trigger(tr); } + +#define _SET(a, b) SET_FUNC(PacketDropped, a, b) + _SET(packet_size, int); + _SET(raw, std::string); + _SET(trigger, Triggered); + _SET(packet_type, PacketType) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "packet_dropped"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::key_unavailable: + return "key_unavailable"; + case Triggered::unknown_connection_id: + return "unknown_connection_id"; + case Triggered::header_decrypt_error: + return "header_decrypt_error"; + case Triggered::payload_decrypt_error: + return "payload_decrypt_error"; + case Triggered::protocol_violation: + return "protocol_violation"; + case Triggered::dos_prevention: + return "dos_prevention"; + case Triggered::unsupported_version: + return "unsupported_version"; + case Triggered::unexpected_packet: + return "unexpected_packet"; + case Triggered::unexpected_source_connection_id: + return "unexpected_source_connection_id"; + case Triggered::unexpected_version: + return "unexpected_version"; + default: + return nullptr; + } + } + }; + + class PacketBuffered : public TransportEvent + { + public: + enum class Triggered : uint8_t { + unknown, + backpressure, + keys_unavailable, + }; + + PacketBuffered(Triggered tr = Triggered::unknown) { set_trigger(tr); } + +#define _SET(a, b) SET_FUNC(PacketBuffered, a, b) + _SET(trigger, Triggered); + _SET(packet_type, PacketType) + _SET(packet_number, std::string) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "packet_buffered"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::backpressure: + return "backpressure"; + case Triggered::keys_unavailable: + return "keys_unavailable"; + default: + return nullptr; + } + } + }; + + class DatagramsEvent : public TransportEvent + { + public: +#define _SET(a, b) SET_FUNC(DatagramsEvent, a, b) + _SET(count, int); + _SET(byte_length, int); +#undef _SET + void encode(YAML::Node &) override; + }; + + class DatagramsSent : public DatagramsEvent + { + public: + std::string + event() const override + { + return "datagrams_sent"; + } + }; + class DatagramReceived : public DatagramsEvent + { + public: + std::string + event() const override + { + return "datagrams_received"; + } + }; + + class DatagramsDropped : public TransportEvent + { + public: +#define _SET(a, b) SET_FUNC(DatagramsDropped, a, b) + _SET(byte_length, int); +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "datagrams_dropped"; + } + }; + + class StreamStateUpdated : public TransportEvent + { + enum class StreamState { + // bidirectional stream states, draft-23 3.4. + idle, + open, + half_closed_local, + half_closed_remote, + closed, + + // sending-side stream states, draft-23 3.1. + ready, + send, + data_sent, + reset_sent, + reset_received, + + // receive-side stream states, draft-23 3.2. + receive, + size_known, + data_read, + reset_read, + + // both-side states + data_received, + + // qlog-defined + destroyed // memory actually freed + }; + + StreamStateUpdated(std::string stream_id, StreamState n) { set_new(n).set_stream_id(stream_id); } + + void encode(YAML::Node &) override; + +#define _SET(a, b) SET_FUNC(StreamStateUpdated, a, b) + _SET(new, StreamState); + _SET(old, StreamState); + _SET(stream_id, std::string); + _SET(bidi, bool); +#undef _SET + + std::string + event() const override + { + return "stream_state_updated"; + } + }; + + class FrameProcessed : public TransportEvent + { + public: + APPEND_FRAME_FUNC(FrameProcessed) + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "frame_processed"; + } + }; + +} // namespace Transport + +namespace Recovery +{ + class RecoveryEvent : public QLogEvent + { + public: + std::string + category() const override + { + return "recovery"; + } + }; + + class ParametersSet : public RecoveryEvent + { + public: +#define _SET(a, b) SET_FUNC(ParametersSet, a, b) + _SET(reordering_threshold, int); + _SET(time_threshold, int); + _SET(timer_granularity, int); + _SET(initial_rtt, int); + _SET(max_datagram_size, int); + _SET(initial_congestion_window, int); + _SET(minimum_congestion_window, int); + _SET(loss_reduction_factor, int); + _SET(persistent_congestion_threshold, int); +#undef _SET + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "parameters_set"; + } + }; + + class MetricsUpdated : public RecoveryEvent + { + public: +#define _SET(a, b) SET_FUNC(MetricsUpdated, a, b) + _SET(min_rtt, int); + _SET(smoothed_rtt, int); + _SET(latest_rtt, int); + _SET(rtt_variance, int); + _SET(max_ack_delay, int); + _SET(pto_count, int); + _SET(congestion_window, int); + _SET(bytes_in_flight, int); + _SET(ssthresh, int); + _SET(packets_in_flight, int); + _SET(in_recovery, int); + _SET(pacing_rate, int); +#undef _SET + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "metrics_updated"; + } + }; + + class CongestionStateUpdated : public RecoveryEvent + { + public: + enum class State : uint8_t { + slow_start, + congestion_avoidance, + application_limited, + recovery, + }; + + enum class Triggered : uint8_t { + unknown, + persistent_congestion, + ECN, + }; + + CongestionStateUpdated(State n, Triggered tr = Triggered::unknown) { set_trigger(tr).set_new(n); } + +#define _SET(a, b) SET_FUNC(CongestionStateUpdated, a, b) + _SET(trigger, Triggered) + _SET(new, State) + _SET(old, State) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "congestion_state_updated"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::persistent_congestion: + return "persistent_congestion"; + case Triggered::ECN: + return "ECN"; + default: + return nullptr; + } + } + + static const char * + state_to_string(State s) + { + switch (s) { + case State::slow_start: + return "slow_start"; + case State::congestion_avoidance: + return "congestion_avoidance"; + case State::application_limited: + return "application_limited"; + case State::recovery: + return "recovery"; + default: + break; + } + + return nullptr; + } + }; + + class LossTimerUpdated : public RecoveryEvent + { + public: + enum class EventType : uint8_t { + set, + expired, + cancelled, + }; + + void + set_timer_type(bool ack) + { + this->_timer_type_ack = ack; + } + + void encode(YAML::Node &) override; + +#define _SET(a, b) SET_FUNC(LossTimerUpdated, a, b) + _SET(event_type, EventType) + _SET(packet_number_space, int); + _SET(delta, int); +#undef _SET + + std::string + event() const override + { + return "loss_timer_updated"; + } + + static const char * + event_type_name(EventType et) + { + switch (et) { + case EventType::set: + return "set"; + case EventType::expired: + return "expired"; + case EventType::cancelled: + return "cancelled"; + default: + break; + } + return nullptr; + } + + private: + bool _timer_type_ack = false; + }; + + class PacketLost : public RecoveryEvent + { + public: + enum class Triggered : uint8_t { + unknown, + reordering_threshold, + time_threshold, + pto_expired, + }; + + PacketLost(PacketType pt, uint64_t pn, Triggered tr = Triggered::unknown) + { + set_trigger(tr).set_packet_type(pt).set_packet_number(pn); + } + +#define _SET(a, b) SET_FUNC(PacketLost, a, b) + _SET(header, PacketHeader) + _SET(packet_number, uint64_t); + _SET(packet_type, PacketType); + _SET(trigger, Triggered) + APPEND_FRAME_FUNC(PacketLost) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "packet_lost"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::pto_expired: + return "pto_expired"; + case Triggered::reordering_threshold: + return "reordering_threshold"; + case Triggered::time_threshold: + return "time_threshold"; + default: + return nullptr; + } + } + }; + + class MarkedForRetransmit : public RecoveryEvent + { + public: + APPEND_FRAME_FUNC(MarkedForRetransmit) + void encode(YAML::Node &) override; + std::string + event() const override + { + return "marked_for_retransmit"; + } + }; + +} // namespace Recovery + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogFrame.cc b/iocore/net/quic/qlog/QLogFrame.cc new file mode 100644 index 00000000000..bcc46fcf739 --- /dev/null +++ b/iocore/net/quic/qlog/QLogFrame.cc @@ -0,0 +1,282 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QLogFrame.h" + +namespace QLog +{ +template +const Real & +Convert(const QUICFrame *frame) +{ + // FIXME: dangerous + auto tmp = static_cast(frame); +#if defined(DEBUG) + auto ref = dynamic_cast(tmp); + ink_assert(ref != nullptr); + return *ref; +#endif + return *static_cast(tmp); +} + +QLogFrameUPtr +QLogFrameFactory::create(const QUICFrame *frame) +{ + switch (frame->type()) { + case QUICFrameType::ACK: + return std::make_unique(Convert(frame)); + case QUICFrameType::PADDING: + return std::make_unique(Convert(frame)); + case QUICFrameType::PING: + return std::make_unique(Convert(frame)); + case QUICFrameType::RESET_STREAM: + return std::make_unique(Convert(frame)); + case QUICFrameType::STOP_SENDING: + return std::make_unique(Convert(frame)); + case QUICFrameType::CRYPTO: + return std::make_unique(Convert(frame)); + case QUICFrameType::NEW_TOKEN: + return std::make_unique(Convert(frame)); + case QUICFrameType::STREAM: + return std::make_unique(Convert(frame)); + case QUICFrameType::MAX_DATA: + return std::make_unique(Convert(frame)); + case QUICFrameType::MAX_STREAM_DATA: + return std::make_unique(Convert(frame)); + case QUICFrameType::MAX_STREAMS: + return std::make_unique(Convert(frame)); + case QUICFrameType::DATA_BLOCKED: + return std::make_unique(Convert(frame)); + case QUICFrameType::STREAM_DATA_BLOCKED: + return std::make_unique(Convert(frame)); + case QUICFrameType::STREAMS_BLOCKED: + return std::make_unique(Convert(frame)); + case QUICFrameType::NEW_CONNECTION_ID: + return std::make_unique(Convert(frame)); + case QUICFrameType::RETIRE_CONNECTION_ID: + return std::make_unique(Convert(frame)); + case QUICFrameType::PATH_CHALLENGE: + return std::make_unique(Convert(frame)); + case QUICFrameType::PATH_RESPONSE: + return std::make_unique(Convert(frame)); + case QUICFrameType::CONNECTION_CLOSE: + return std::make_unique(Convert(frame)); + case QUICFrameType::HANDSHAKE_DONE: + return std::make_unique(Convert(frame)); + default: + ink_release_assert(0); + return nullptr; + } +} + +namespace Frame +{ + template + std::string + convert_to_string(T a) + { + return std::to_string(static_cast(a)); + } + + void + AckFrame::encode(YAML::Node &node) + { + node["frame_type"] = "ack"; + node["ack_delay"] = std::to_string(ack_delay); + for (auto &it : acked_range) { + YAML::Node sub; + sub.push_back(convert_to_string(it.first())); + sub.push_back(convert_to_string(it.last())); + node["acked_ranges"].push_back(sub); + } + + if (ect1) { + node["ect1"] = ect1; + } + + if (ect1) { + node["ect0"] = ect0; + } + + if (ce) { + node["ce"] = ce; + } + } + + void + StreamFrame::encode(YAML::Node &node) + { + node["frame_type"] = "stream"; + node["stream_id"] = stream_id; + node["offset"] = offset; + node["length"] = length; + node["fin"] = fin; + } + + void + PaddingFrame::encode(YAML::Node &node) + { + node["frame_type"] = "padding"; + } + + void + PingFrame::encode(YAML::Node &node) + { + node["frame_type"] = "ping"; + } + + void + RstStreamFrame::encode(YAML::Node &node) + { + node["frame_type"] = "reset_stream"; + node["stream_id"] = stream_id; + node["error_code"] = error_code; + node["final_size"] = final_size; + } + + void + StopSendingFrame::encode(YAML::Node &node) + { + node["frame_type"] = "stop_sending"; + node["stream_id"] = stream_id; + node["error_code"] = error_code; + } + + void + CryptoFrame::encode(YAML::Node &node) + { + node["frame_type"] = "crypto"; + node["offset"] = offset; + node["length"] = length; + } + + void + NewTokenFrame::encode(YAML::Node &node) + { + node["frame_type"] = "new_token"; + node["token"] = token; + node["length"] = length; + } + + void + MaxDataFrame::encode(YAML::Node &node) + { + node["frame_type"] = "max_data"; + node["maximum"] = maximum; + } + + void + MaxStreamDataFrame::encode(YAML::Node &node) + { + node["frame_type"] = "max_stream_data"; + node["maximum"] = maximum; + node["stream_id"] = stream_id; + } + + void + MaxStreamsFrame::encode(YAML::Node &node) + { + node["frame_type"] = "max_streams"; + node["maximum"] = maximum; + node["stream_type"] = stream_type; + } + + void + DataBlockedFrame::encode(YAML::Node &node) + { + node["frame_type"] = "data_blocked"; + node["limit"] = limit; + } + + void + StreamDataBlockedFrame::encode(YAML::Node &node) + { + node["frame_type"] = "stream_data_blocked"; + node["limit"] = limit; + node["stream_id"] = stream_id; + } + + void + StreamsBlockedFrame::encode(YAML::Node &node) + { + node["frame_type"] = "streams_blocked"; + node["stream_id"] = stream_id; + node["stream_type"] = stream_type; + } + + void + NewConnectionIDFrame::encode(YAML::Node &node) + { + node["frame_type"] = "new_connection_id"; + node["sequence_number"] = sequence_number; + node["retire_prior_to"] = retire_prior_to; + node["stateless_reset_token"] = stateless_reset_token; + node["length"] = length; + } + + void + RetireConnectionIDFrame::encode(YAML::Node &node) + { + node["frame_type"] = "retire_connection_id"; + node["sequence_number"] = sequence_number; + } + + void + PathChallengeFrame::encode(YAML::Node &node) + { + node["frame_type"] = "path_challenge"; + node["data"] = data; + } + + void + PathResponseFrame::encode(YAML::Node &node) + { + node["frame_type"] = "path_response"; + node["data"] = data; + } + + void + ConnectionCloseFrame::encode(YAML::Node &node) + { + node["frame_type"] = "connection_close"; + node["error_space"] = error_space; + node["error_code"] = error_code; + node["raw_error_code"] = raw_error_code; + node["reason"] = reason; + } + + void + HandshakeDoneFrame::encode(YAML::Node &node) + { + node["frame_type"] = "handshake_done"; + } + + void + UnknownFrame::encode(YAML::Node &node) + { + node["frame_type"] = "unknown"; + node["raw_frame_type"] = raw_frame_type; + } + +} // namespace Frame +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogFrame.h b/iocore/net/quic/qlog/QLogFrame.h new file mode 100644 index 00000000000..be56351a1ee --- /dev/null +++ b/iocore/net/quic/qlog/QLogFrame.h @@ -0,0 +1,309 @@ +/** @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 +#include + +#include "QUICFrame.h" + +namespace QLog +{ +class QLogFrame +{ +public: + QLogFrame(QUICFrameType type) : _type(type) {} + virtual ~QLogFrame() {} + + QUICFrameType + type() const + { + return this->_type; + } + + // encode frame into YAML stype + virtual void encode(YAML::Node &node) = 0; + +protected: + QUICFrameType _type = QUICFrameType::UNKNOWN; +}; + +using QLogFrameUPtr = std::unique_ptr; + +// +// convert QUICFrame to QLogFrame +// +class QLogFrameFactory +{ +public: + // create QLogFrame + static QLogFrameUPtr create(const QUICFrame *frame); +}; + +namespace Frame +{ + struct AckFrame : public QLogFrame { + AckFrame(const QUICAckFrame &frame) : QLogFrame(frame.type()) + { + acked_range = frame.ranges(); + ack_delay = frame.ack_delay(); + if (frame.ecn_section()) { + ect0 = frame.ecn_section()->ect0_count(); + ect1 = frame.ecn_section()->ect1_count(); + ce = frame.ecn_section()->ecn_ce_count(); + } + } + + void encode(YAML::Node &) override; + + std::set acked_range; + uint64_t ect1 = 0; + uint64_t ect0 = 0; + uint64_t ce = 0; + uint64_t ack_delay = 0; + }; + + struct StreamFrame : public QLogFrame { + StreamFrame(const QUICStreamFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + offset = std::to_string(static_cast(frame.offset())); + length = frame.data_length(); + fin = frame.has_fin_flag(); + } + + void encode(YAML::Node &) override; + std::string stream_id; + + // These two MUST always be set + // If not present in the Frame type, log their default values + std::string offset; + uint64_t length = 0; + + // this MAY be set any time, but MUST only be set if the value is "true" + // if absent, the value MUST be assumed to be "false" + bool fin = false; + + // FIXME raw + }; + + struct PaddingFrame : public QLogFrame { + PaddingFrame(const QUICPaddingFrame &frame) : QLogFrame(frame.type()) {} + void encode(YAML::Node &) override; + }; + + struct PingFrame : public QLogFrame { + PingFrame(const QUICPingFrame &frame) : QLogFrame(frame.type()) {} + void encode(YAML::Node &) override; + }; + + struct RstStreamFrame : public QLogFrame { + RstStreamFrame(const QUICRstStreamFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + error_code = frame.error_code(); + final_size = std::to_string(frame.final_offset()); + } + + void encode(YAML::Node &) override; + std::string stream_id; + // FIXME ApplicationError + uint64_t error_code = 0; + std::string final_size; + }; + + struct StopSendingFrame : public QLogFrame { + StopSendingFrame(const QUICStopSendingFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + error_code = frame.error_code(); + } + + void encode(YAML::Node &) override; + std::string stream_id; + // FIXME ApplicationError + uint64_t error_code = 0; + }; + + struct CryptoFrame : public QLogFrame { + CryptoFrame(const QUICCryptoFrame &frame) : QLogFrame(frame.type()) + { + offset = std::to_string(static_cast(frame.offset())); + length = frame.data_length(); + } + + void encode(YAML::Node &) override; + std::string offset; + uint64_t length = 0; + }; + + struct NewTokenFrame : public QLogFrame { + NewTokenFrame(const QUICNewTokenFrame &frame) : QLogFrame(frame.type()) + { + token = QUICBase::to_hex(frame.token(), frame.token_length()); + length = frame.token_length(); + } + + void encode(YAML::Node &) override; + std::string token; + uint64_t length = 0; + }; + + struct MaxDataFrame : public QLogFrame { + MaxDataFrame(const QUICMaxDataFrame &frame) : QLogFrame(frame.type()) { maximum = std::to_string(frame.maximum_data()); } + + void encode(YAML::Node &) override; + std::string maximum; + }; + + struct MaxStreamDataFrame : public QLogFrame { + MaxStreamDataFrame(const QUICMaxStreamDataFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + maximum = std::to_string(frame.maximum_stream_data()); + } + + void encode(YAML::Node &) override; + std::string stream_id; + std::string maximum; + }; + + struct MaxStreamsFrame : public QLogFrame { + MaxStreamsFrame(const QUICMaxStreamsFrame &frame) : QLogFrame(frame.type()) + { + maximum = std::to_string(frame.maximum_streams()); + // FIXME + stream_type = "bidirectional"; + } + + void encode(YAML::Node &) override; + std::string stream_type; + std::string maximum; + }; + + struct DataBlockedFrame : public QLogFrame { + DataBlockedFrame(const QUICDataBlockedFrame &frame) : QLogFrame(frame.type()) + { + limit = std::to_string(static_cast(frame.offset())); + } + void encode(YAML::Node &) override; + std::string limit; + }; + + struct StreamDataBlockedFrame : public QLogFrame { + StreamDataBlockedFrame(const QUICStreamDataBlockedFrame &frame) : QLogFrame(frame.type()) + { + limit = std::to_string(static_cast(frame.offset())); + stream_id = std::to_string(static_cast(frame.stream_id())); + } + + void encode(YAML::Node &) override; + std::string stream_id, limit; + }; + + struct StreamsBlockedFrame : public QLogFrame { + StreamsBlockedFrame(const QUICStreamIdBlockedFrame &frame) : QLogFrame(frame.type()) + { + stream_type = "bidirectional"; + stream_id = std::to_string(static_cast(frame.stream_id())); + } + + void encode(YAML::Node &) override; + std::string stream_id, stream_type; + }; + + struct NewConnectionIDFrame : public QLogFrame { + NewConnectionIDFrame(const QUICNewConnectionIdFrame &frame) : QLogFrame(frame.type()) + { + sequence_number = std::to_string(frame.sequence()); + retire_prior_to = std::to_string(frame.retire_prior_to()); + connection_id = frame.connection_id().hex(); + stateless_reset_token = QUICBase::to_hex(frame.stateless_reset_token().buf(), QUICStatelessResetToken::LEN); + length = frame.connection_id().length(); + } + + void encode(YAML::Node &) override; + std::string sequence_number, retire_prior_to, connection_id, stateless_reset_token; + uint8_t length = 0; + }; + + struct RetireConnectionIDFrame : public QLogFrame { + RetireConnectionIDFrame(const QUICRetireConnectionIdFrame &frame) : QLogFrame(frame.type()) + { + sequence_number = std::to_string(frame.seq_num()); + } + void encode(YAML::Node &) override; + std::string sequence_number; + }; + + struct PathChallengeFrame : public QLogFrame { + PathChallengeFrame(const QUICPathChallengeFrame &frame) : QLogFrame(frame.type()) + { + data = QUICBase::to_hex(frame.data(), QUICPathChallengeFrame::DATA_LEN); + } + void encode(YAML::Node &) override; + std::string data; + }; + + struct PathResponseFrame : public QLogFrame { + PathResponseFrame(const QUICPathResponseFrame &frame) : QLogFrame(frame.type()) + { + data = QUICBase::to_hex(frame.data(), QUICPathChallengeFrame::DATA_LEN); + } + void encode(YAML::Node &) override; + std::string data; + }; + + struct ConnectionCloseFrame : public QLogFrame { + ConnectionCloseFrame(const QUICConnectionCloseFrame &frame, bool app = false) : QLogFrame(frame.type()) + { + error_space = app ? "application" : "transport"; + error_code = frame.error_code(); + // FIXME + raw_error_code = error_code; + reason = frame.reason_phrase(); + } + + void encode(YAML::Node &) override; + std::string error_space, reason, trigger_frame_type; + uint64_t error_code, raw_error_code; + }; + + struct HandshakeDoneFrame : public QLogFrame { + HandshakeDoneFrame(const QUICHandshakeDoneFrame &frame) : QLogFrame(frame.type()){}; + void encode(YAML::Node &) override; + }; + + struct UnknownFrame : public QLogFrame { + UnknownFrame(const QUICUnknownFrame &frame) : QLogFrame(frame.type()) + { + // FIXME + raw_frame_type = static_cast(frame.type()); + } + + void encode(YAML::Node &) override; + uint8_t raw_frame_type = 0; + }; +} // namespace Frame +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogListener.h b/iocore/net/quic/qlog/QLogListener.h new file mode 100644 index 00000000000..7b55af2ceff --- /dev/null +++ b/iocore/net/quic/qlog/QLogListener.h @@ -0,0 +1,119 @@ +/** @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 "QLog.h" +#include "QLogUtils.h" +#include "QUICPacket.h" +#include "QUICContext.h" + +namespace QLog +{ +class QLogListener : public QUICCallback +{ +public: + QLogListener(QUICContext &ctx, std::string odcid, std::string title = "", std::string desc = "") : _context(ctx) + { + this->_log.new_trace(odcid, title, desc); // initial new trace + } + + void + frame_recv_callback(QUICCallbackContext &, const QUICFrame &frame) override + { + this->_recv_frames.push_back(QLogFrameFactory::create(static_cast(&frame))); + } + + void + frame_packetize_callback(QUICCallbackContext &, const QUICFrame &frame) override + { + this->_send_frames.push_back(QLogFrameFactory::create(static_cast(&frame))); + } + + void + packet_send_callback(QUICCallbackContext &, const QUICPacket &packet) override + { + auto qe = std::make_unique(PacketTypeToName(packet.type()), QUICPacketToLogPacket(packet)); + for (auto &it : this->_send_frames) { + qe->append_frames(std::move(it)); + } + this->_send_frames.clear(); + this->_log.last_trace().push_event(std::move(qe)); + }; + + void + packet_recv_callback(QUICCallbackContext &, const QUICPacket &packet) override + { + auto qe = std::make_unique(PacketTypeToName(packet.type()), QUICPacketToLogPacket(packet)); + for (auto &it : this->_recv_frames) { + qe->append_frames(std::move(it)); + } + this->_recv_frames.clear(); + this->_log.last_trace().push_event(std::move(qe)); + }; + + void + packet_lost_callback(QUICCallbackContext &, const QUICPacketInfo &packet) override + { + auto qe = std::make_unique(PacketTypeToName(packet.type), packet.packet_number); + this->_log.last_trace().push_event(std::move(qe)); + }; + + void + cc_metrics_update_callback(QUICCallbackContext &, uint64_t congestion_window, uint64_t bytes_in_flight, uint64_t sshresh) override + { + auto qe = std::make_unique(); + qe->set_congestion_window(static_cast(congestion_window)).set_bytes_in_flight(bytes_in_flight).set_ssthresh(sshresh); + this->_log.last_trace().push_event(std::move(qe)); + } + + void + congestion_state_updated_callback(QUICCallbackContext &, QUICCongestionController::State state) override + { + if (state != this->_state) { + this->_log.last_trace().push_event(std::make_unique(CongestionStateConvert(state))); + this->_state = state; + } + } + + void + connection_close_callback(QUICCallbackContext &) override + { + this->_log.dump(this->_context.config()->qlog_dir()); + } + + Trace & + last_trace() + { + return this->_log.last_trace(); + } + +private: + QUICCongestionController::State _state = QUICCongestionController::State::SLOW_START; + std::vector _recv_frames; + std::vector _send_frames; + QLog _log; + QUICContext &_context; +}; + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogUtils.h b/iocore/net/quic/qlog/QLogUtils.h new file mode 100644 index 00000000000..2242a4fd6e4 --- /dev/null +++ b/iocore/net/quic/qlog/QLogUtils.h @@ -0,0 +1,80 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QLog.h" +#include "QUICPacket.h" + +namespace QLog +{ +inline static const char * +PacketTypeToName(QUICPacketType pt) +{ + switch (pt) { + case QUICPacketType::INITIAL: + return "initial"; + case QUICPacketType::HANDSHAKE: + return "handshake"; + case QUICPacketType::ZERO_RTT_PROTECTED: + return "0rtt"; + case QUICPacketType::PROTECTED: + return "1rtt"; + case QUICPacketType::RETRY: + return "retry"; + case QUICPacketType::VERSION_NEGOTIATION: + return "version_negotiation"; + case QUICPacketType::STATELESS_RESET: + return "stateless_reset"; + default: + return "unknown"; + } +} + +inline static Recovery::CongestionStateUpdated::State +CongestionStateConvert(QUICCongestionController::State state) +{ + switch (state) { + case QUICCongestionController::State::APPPLICATION_LIMITED: + return Recovery::CongestionStateUpdated::State::application_limited; + case QUICCongestionController::State::SLOW_START: + return Recovery::CongestionStateUpdated::State::slow_start; + case QUICCongestionController::State::CONGESTION_AVOIDANCE: + return Recovery::CongestionStateUpdated::State::congestion_avoidance; + case QUICCongestionController::State::RECOVERY: + return Recovery::CongestionStateUpdated::State::recovery; + default: + return Recovery::CongestionStateUpdated::State::slow_start; + } +} + +inline static PacketHeader +QUICPacketToLogPacket(const QUICPacket &packet) +{ + PacketHeader ph; + ph.dcid = packet.destination_cid().hex(); + ph.packet_number = std::to_string(packet.packet_number()); + ph.packet_size = packet.size(); + ph.payload_length = packet.payload_length(); + return ph; +} + +} // namespace QLog diff --git a/iocore/net/quic/test/main.cc b/iocore/net/quic/test/main.cc index eb93ea784b6..4e7ee5adc6f 100644 --- a/iocore/net/quic/test/main.cc +++ b/iocore/net/quic/test/main.cc @@ -30,6 +30,7 @@ #include "tscore/Diags.h" #include "RecordsConfig.h" +#include "QUICGlobals.h" #include "QUICConfig.h" struct EventProcessorListener : Catch::TestEventListenerBase { @@ -49,6 +50,7 @@ struct EventProcessorListener : Catch::TestEventListenerBase { LibRecordsConfigInit(); QUICConfig::startup(); + QUIC::init(); EThread *thread = new EThread(); thread->set_specific(); diff --git a/iocore/net/quic/test/test_QUICAckFrameCreator.cc b/iocore/net/quic/test/test_QUICAckFrameCreator.cc index 39b7bc9db4d..c60ecd9c765 100644 --- a/iocore/net/quic/test/test_QUICAckFrameCreator.cc +++ b/iocore/net/quic/test/test_QUICAckFrameCreator.cc @@ -133,11 +133,34 @@ TEST_CASE("QUICAckFrameManager should send", "[quic]") ack_manager.update(level, 0, 1, false); CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); + ack_manager.update(level, 1, 1, true); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); + + ack_manager.update(level, 3, 1, false); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true); + } + + SECTION("QUIC every two ack eliciting packet", "[quic]") + { + QUICAckFrameManager ack_manager; + uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + + QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; + ack_manager.update(level, 0, 1, false); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); + ack_manager.update(level, 1, 1, false); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true); + + CHECK(ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0) != nullptr); + + ack_manager.update(level, 2, 1, false); CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); ack_manager.update(level, 3, 1, false); CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true); + + CHECK(ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0) != nullptr); } SECTION("QUIC delay too much time", "[quic]") diff --git a/iocore/net/quic/test/test_QUICFrame.cc b/iocore/net/quic/test/test_QUICFrame.cc index 4460066ba5b..ec7a4324e00 100644 --- a/iocore/net/quic/test/test_QUICFrame.cc +++ b/iocore/net/quic/test/test_QUICFrame.cc @@ -68,8 +68,10 @@ TEST_CASE("QUICFrame Type", "[quic]") CHECK(QUICFrame::type(reinterpret_cast("\x1c")) == QUICFrameType::CONNECTION_CLOSE); CHECK(QUICFrame::type(reinterpret_cast("\x1d")) == QUICFrameType::CONNECTION_CLOSE); + CHECK(QUICFrame::type(reinterpret_cast("\x1e")) == QUICFrameType::HANDSHAKE_DONE); + // Undefined ragne - CHECK(QUICFrame::type(reinterpret_cast("\x1e")) == QUICFrameType::UNKNOWN); + CHECK(QUICFrame::type(reinterpret_cast("\x1f")) == QUICFrameType::UNKNOWN); CHECK(QUICFrame::type(reinterpret_cast("\xff")) == QUICFrameType::UNKNOWN); } @@ -826,6 +828,41 @@ TEST_CASE("Store Padding Frame", "[quic]") CHECK(memcmp(buf, expected, len) == 0); } +TEST_CASE("Load HandshakeDone Frame", "[quic]") +{ + uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + uint8_t buf[] = { + 0x1e, // Type + }; + const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr); + CHECK(frame->type() == QUICFrameType::HANDSHAKE_DONE); + CHECK(frame->size() == 1); + + const QUICHandshakeDoneFrame *handshake_done_frame = static_cast(frame); + CHECK(handshake_done_frame != nullptr); +} + +TEST_CASE("Store HandshakeDone Frame", "[quic]") +{ + uint8_t buf[16]; + size_t len = 0; + + uint8_t expected[] = { + 0x1e, // Type + }; + + QUICHandshakeDoneFrame frame; + CHECK(frame.size() == 1); + + Ptr ibb = frame.to_io_buffer_block(sizeof(buf)); + for (auto b = ibb; b; b = b->next) { + memcpy(buf + len, b->start(), b->size()); + len += b->size(); + } + CHECK(len == 1); + CHECK(memcmp(buf, expected, len) == 0); +} + TEST_CASE("ConnectionClose Frame", "[quic]") { uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; diff --git a/iocore/net/quic/test/test_QUICFrameDispatcher.cc b/iocore/net/quic/test/test_QUICFrameDispatcher.cc index e34322bad80..10e2d318804 100644 --- a/iocore/net/quic/test/test_QUICFrameDispatcher.cc +++ b/iocore/net/quic/test/test_QUICFrameDispatcher.cc @@ -39,7 +39,7 @@ TEST_CASE("QUICFrameHandler", "[quic]") MockQUICContext context; MockQUICConnection connection; - MockQUICStreamManager streamManager = {&connection}; + MockQUICStreamManager streamManager = {&context}; MockQUICConnectionInfoProvider info; MockQUICLossDetector lossDetector(context); @@ -62,7 +62,8 @@ TEST_CASE("QUICFrameHandler", "[quic]") } bool should_send_ack; bool is_flow_controlled; - quicFrameDispatcher.receive_frames(QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, nullptr); + quicFrameDispatcher.receive_frames(context, QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, + nullptr); CHECK(connection.getTotalFrameCount() == 0); CHECK(streamManager.getTotalFrameCount() == 1); @@ -74,7 +75,8 @@ TEST_CASE("QUICFrameHandler", "[quic]") memcpy(buf + len, b->start(), b->size()); len += b->size(); } - quicFrameDispatcher.receive_frames(QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, nullptr); + quicFrameDispatcher.receive_frames(context, QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, + nullptr); CHECK(connection.getTotalFrameCount() == 1); CHECK(streamManager.getTotalFrameCount() == 1); } diff --git a/iocore/net/quic/test/test_QUICFrameRetransmitter.cc b/iocore/net/quic/test/test_QUICFrameRetransmitter.cc index c8a91a5a1ae..916c65f5ede 100644 --- a/iocore/net/quic/test/test_QUICFrameRetransmitter.cc +++ b/iocore/net/quic/test/test_QUICFrameRetransmitter.cc @@ -189,7 +189,7 @@ TEST_CASE("QUICFrameRetransmitter successfully split crypto frame", "[quic]") info->level = QUICEncryptionLevel::INITIAL; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), data, sizeof(data)); block->fill(sizeof(data)); @@ -243,7 +243,7 @@ TEST_CASE("QUICFrameRetransmitter successfully split stream frame with fin flag" info->level = QUICEncryptionLevel::INITIAL; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), data, sizeof(data)); block->fill(sizeof(data)); diff --git a/iocore/net/quic/test/test_QUICHandshakeProtocol.cc b/iocore/net/quic/test/test_QUICHandshakeProtocol.cc index f90b10af3fa..4a71a07be96 100644 --- a/iocore/net/quic/test/test_QUICHandshakeProtocol.cc +++ b/iocore/net/quic/test/test_QUICHandshakeProtocol.cc @@ -45,9 +45,6 @@ struct PollCont; #include "P_UnixNet.h" #include "P_UnixNetVConnection.h" -// depends on size of cert -static constexpr uint32_t MAX_HANDSHAKE_MSG_LEN = 8192; - #include "./server_cert.h" static void @@ -115,49 +112,52 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketPayloadProtector ppp_client(pp_key_info_client); QUICPacketPayloadProtector ppp_server(pp_key_info_server); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + 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})); // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(client->handshake(&msg1, nullptr) == 1); + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; + + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); std::cout << "### Messages from client" << std::endl; - print_hex(msg1.buf, msg1.offsets[4]); + print_hex(msg1->buf, msg1->offsets[4]); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); std::cout << "### Messages from server" << std::endl; - print_hex(msg2.buf, msg2.offsets[4]); + print_hex(msg2->buf, msg2->offsets[4]); // FIN - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg3; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg2_1; uint8_t msg2_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg2_1.buf = msg2_1_buf; msg2_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg2_1.buf, msg2.buf, msg2.offsets[1]); + memcpy(msg2_1.buf, msg2->buf, msg2->offsets[1]); msg2_1.offsets[0] = 0; - msg2_1.offsets[1] = msg2.offsets[1]; - msg2_1.offsets[2] = msg2.offsets[1]; - msg2_1.offsets[3] = msg2.offsets[1]; - msg2_1.offsets[4] = msg2.offsets[1]; + msg2_1.offsets[1] = msg2->offsets[1]; + msg2_1.offsets[2] = msg2->offsets[1]; + msg2_1.offsets[3] = msg2->offsets[1]; + msg2_1.offsets[4] = msg2->offsets[1]; // EE - FIN QUICHandshakeMsgs msg2_2; @@ -165,8 +165,8 @@ TEST_CASE("QUICHandshakeProtocol") msg2_2.buf = msg2_2_buf; msg2_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg2.offsets[3] - msg2.offsets[2]; - memcpy(msg2_2.buf, msg2.buf + msg2.offsets[1], len); + size_t len = msg2->offsets[3] - msg2->offsets[2]; + memcpy(msg2_2.buf, msg2->buf + msg2->offsets[1], len); msg2_2.offsets[0] = 0; msg2_2.offsets[1] = 0; msg2_2.offsets[2] = 0; @@ -175,29 +175,19 @@ TEST_CASE("QUICHandshakeProtocol") REQUIRE(client->handshake(&msg3, &msg2_1) == 1); REQUIRE(client->handshake(&msg3, &msg2_2) == 1); -#else - REQUIRE(client->handshake(&msg3, &msg2) == 1); -#endif std::cout << "### Messages from client" << std::endl; - print_hex(msg3.buf, msg3.offsets[4]); + print_hex(msg3->buf, msg3->offsets[4]); // NS - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); std::cout << "### Messages from server" << std::endl; - print_hex(msg4.buf, msg4.offsets[4]); + print_hex(msg4->buf, msg4->offsets[4]); - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - REQUIRE(client->handshake(&msg5, &msg4) == 1); - std::cout << "### Messages from server" << std::endl; - print_hex(msg4.buf, msg4.offsets[4]); + QUICHandshakeMsgs *msg5 = nullptr; + REQUIRE(client->handshake(&msg5, msg4) == 1); + REQUIRE(msg5 == nullptr); // encrypt - decrypt // client (encrypt) - server (decrypt) @@ -247,69 +237,66 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketPayloadProtector ppp_client(pp_key_info_client); QUICPacketPayloadProtector ppp_server(pp_key_info_server); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + 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})); // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(client->handshake(&msg1, nullptr) == 1); + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; + + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); std::cout << "### Messages from client" << std::endl; - print_hex(msg1.buf, msg1.offsets[4]); + print_hex(msg1->buf, msg1->offsets[4]); // HRR - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); std::cout << "### Messages from server" << std::endl; - print_hex(msg2.buf, msg2.offsets[4]); + print_hex(msg2->buf, msg2->offsets[4]); // CH - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(client->handshake(&msg3, &msg2) == 1); + QUICHandshakeMsgs *msg3 = nullptr; + REQUIRE(client->handshake(&msg3, msg2) == 1); + REQUIRE(msg3); std::cout << "### Messages from client" << std::endl; - print_hex(msg3.buf, msg3.offsets[4]); + print_hex(msg3->buf, msg3->offsets[4]); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); std::cout << "### Messages from server" << std::endl; - print_hex(msg4.buf, msg4.offsets[4]); + print_hex(msg4->buf, msg4->offsets[4]); // FIN - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg5 = nullptr; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg4_1; uint8_t msg4_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg4_1.buf = msg4_1_buf; msg4_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg4_1.buf, msg4.buf, msg4.offsets[1]); + memcpy(msg4_1.buf, msg4->buf, msg4->offsets[1]); msg4_1.offsets[0] = 0; - msg4_1.offsets[1] = msg4.offsets[1]; - msg4_1.offsets[2] = msg4.offsets[1]; - msg4_1.offsets[3] = msg4.offsets[1]; - msg4_1.offsets[4] = msg4.offsets[1]; + msg4_1.offsets[1] = msg4->offsets[1]; + msg4_1.offsets[2] = msg4->offsets[1]; + msg4_1.offsets[3] = msg4->offsets[1]; + msg4_1.offsets[4] = msg4->offsets[1]; // EE - FIN QUICHandshakeMsgs msg4_2; @@ -317,8 +304,8 @@ TEST_CASE("QUICHandshakeProtocol") msg4_2.buf = msg4_2_buf; msg4_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg4.offsets[3] - msg4.offsets[2]; - memcpy(msg4_2.buf, msg4.buf + msg4.offsets[1], len); + size_t len = msg4->offsets[3] - msg4->offsets[2]; + memcpy(msg4_2.buf, msg4->buf + msg4->offsets[1], len); msg4_2.offsets[0] = 0; msg4_2.offsets[1] = 0; msg4_2.offsets[2] = 0; @@ -327,21 +314,16 @@ TEST_CASE("QUICHandshakeProtocol") REQUIRE(client->handshake(&msg5, &msg4_1) == 1); REQUIRE(client->handshake(&msg5, &msg4_2) == 1); -#else - REQUIRE(client->handshake(&msg5, &msg4) == 1); -#endif + REQUIRE(msg5); std::cout << "### Messages from client" << std::endl; - print_hex(msg5.buf, msg5.offsets[4]); + print_hex(msg5->buf, msg5->offsets[4]); // NS - QUICHandshakeMsgs msg6; - uint8_t msg6_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg6.buf = msg6_buf; - msg6.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg6, &msg5) == 1); + QUICHandshakeMsgs *msg6 = nullptr; + REQUIRE(server->handshake(&msg6, msg5) == 1); + REQUIRE(msg6); std::cout << "### Messages from server" << std::endl; - print_hex(msg6.buf, msg6.offsets[4]); + print_hex(msg6->buf, msg6->offsets[4]); Ptr original_ibb = make_ptr(new_IOBufferBlock()); original_ibb->set_internal(const_cast(original), sizeof(original), BUFFER_SIZE_NOT_ALLOCATED); @@ -405,13 +387,10 @@ TEST_CASE("QUICHandshakeProtocol") msg1.offsets[3] = msg1_len; msg1.offsets[4] = msg1_len; - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - + QUICHandshakeMsgs *msg2 = nullptr; CHECK(server->handshake(&msg2, &msg1) != 1); - CHECK(msg2.error_code == 0x10a); //< 0x100 + unexpected_message(10) + CHECK(server->has_crypto_error()); + CHECK((server->crypto_error() == 0x10a || server->crypto_error() == 0x150)); //< 0x100 + unexpected_message(10) // Teardown delete server; @@ -425,47 +404,50 @@ TEST_CASE("QUICHandshakeProtocol") 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); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + 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})); // # Start Handshake - // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; - REQUIRE(client->handshake(&msg1, nullptr) == 1); + // CH + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); // FIN - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg3 = nullptr; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg2_1; uint8_t msg2_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg2_1.buf = msg2_1_buf; msg2_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg2_1.buf, msg2.buf, msg2.offsets[1]); + memcpy(msg2_1.buf, msg2->buf, msg2->offsets[1]); msg2_1.offsets[0] = 0; - msg2_1.offsets[1] = msg2.offsets[1]; - msg2_1.offsets[2] = msg2.offsets[1]; - msg2_1.offsets[3] = msg2.offsets[1]; - msg2_1.offsets[4] = msg2.offsets[1]; + msg2_1.offsets[1] = msg2->offsets[1]; + msg2_1.offsets[2] = msg2->offsets[1]; + msg2_1.offsets[3] = msg2->offsets[1]; + msg2_1.offsets[4] = msg2->offsets[1]; // EE - FIN QUICHandshakeMsgs msg2_2; @@ -473,8 +455,8 @@ TEST_CASE("QUICHandshakeProtocol") msg2_2.buf = msg2_2_buf; msg2_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg2.offsets[3] - msg2.offsets[2]; - memcpy(msg2_2.buf, msg2.buf + msg2.offsets[1], len); + size_t len = msg2->offsets[3] - msg2->offsets[2]; + memcpy(msg2_2.buf, msg2->buf + msg2->offsets[1], len); msg2_2.offsets[0] = 0; msg2_2.offsets[1] = 0; msg2_2.offsets[2] = 0; @@ -483,23 +465,15 @@ TEST_CASE("QUICHandshakeProtocol") REQUIRE(client->handshake(&msg3, &msg2_1) == 1); REQUIRE(client->handshake(&msg3, &msg2_2) == 1); -#else - REQUIRE(client->handshake(&msg3, &msg2) == 1); -#endif // NS - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); - - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - REQUIRE(client->handshake(&msg5, &msg4) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); + + QUICHandshakeMsgs *msg5 = nullptr; + REQUIRE(client->handshake(&msg5, msg4) == 1); + REQUIRE(msg5 == nullptr); // # End Handshake diff --git a/iocore/net/quic/test/test_QUICLossDetector.cc b/iocore/net/quic/test/test_QUICLossDetector.cc index 6c3604ffef3..acabd8252ad 100644 --- a/iocore/net/quic/test/test_QUICLossDetector.cc +++ b/iocore/net/quic/test/test_QUICLossDetector.cc @@ -24,6 +24,8 @@ #include "catch.hpp" #include "QUICLossDetector.h" +#include "QUICPacketFactory.h" +#include "QUICAckFrameCreator.h" #include "QUICEvents.h" #include "Mock.h" #include "tscore/ink_hrtime.h" @@ -43,10 +45,11 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") QUICPadder padder(NetVConnectionContext_t::NET_VCONNECTION_IN); MockQUICCongestionController cc; QUICLossDetector detector(context, &cc, &rtt_measure, &pinger, &padder); - ats_unique_buf payload = ats_unique_malloc(512); - size_t payload_len = 512; - QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); - QUICAckFrame *frame = nullptr; + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(512, BUFFER_SIZE_INDEX_32K)); + size_t payload_len = 512; + QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); + QUICAckFrame *frame = nullptr; SECTION("Handshake") { @@ -66,21 +69,21 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") CHECK(len < 4); // Send SERVER_CLEARTEXT (Handshake message) - ats_unique_buf payload = ats_unique_malloc(sizeof(raw)); - memcpy(payload.get(), raw, sizeof(raw)); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(sizeof(raw), BUFFER_SIZE_INDEX_32K)); + memcpy(payload->start(), raw, sizeof(raw)); + payload->fill(sizeof(raw)); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::HANDSHAKE, QUICKeyPhase::HANDSHAKE, - {reinterpret_cast("\xff\xdd\xbb\x99\x77\x55\x33\x11"), 8}, - {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, 0x00000001, 0, 0x00112233, - false, std::move(payload), sizeof(raw)); - QUICPacketUPtr packet = QUICPacketUPtr(new QUICPacket(std::move(header), std::move(payload), sizeof(raw), true, false), - [](QUICPacket *p) { delete p; }); + QUICHandshakePacket *handshake_packet = new QUICHandshakePacket( + 0x00112233, {reinterpret_cast("\xff\xdd\xbb\x99\x77\x55\x33\x11"), 8}, + {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{ packet->packet_number(), Thread::get_hrtime(), packet->is_ack_eliciting(), - packet->is_crypto_packet(), + static_cast(*packet).is_crypto_packet(), true, packet->size(), packet->type(), @@ -105,37 +108,67 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") // Send packet (1) to (7) QUICPacketNumberSpace pn_space = QUICPacketNumberSpace::ApplicationData; QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet1 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_1_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet1 = pf.create_short_header_packet( + packet_1_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); REQUIRE(packet1 != nullptr); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet2 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet3 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet4 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet5 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet6 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet7 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet8 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet9 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet10 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_2_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet2 = pf.create_short_header_packet( + packet_2_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_3_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet3 = pf.create_short_header_packet( + packet_3_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_4_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet4 = pf.create_short_header_packet( + packet_4_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_5_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet5 = pf.create_short_header_packet( + packet_5_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_6_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet6 = pf.create_short_header_packet( + packet_6_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_7_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet7 = pf.create_short_header_packet( + packet_7_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_8_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet8 = pf.create_short_header_packet( + packet_8_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_9_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet9 = pf.create_short_header_packet( + packet_9_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_10_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet10 = pf.create_short_header_packet( + packet_10_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); QUICPacketNumber pn1 = packet1->packet_number(); QUICPacketNumber pn2 = packet2->packet_number(); @@ -152,7 +185,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet1->packet_number(), Thread::get_hrtime(), packet1->is_ack_eliciting(), - packet1->is_crypto_packet(), + false, true, packet1->size(), packet1->type(), @@ -161,7 +194,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet2->packet_number(), Thread::get_hrtime(), packet2->is_ack_eliciting(), - packet2->is_crypto_packet(), + false, true, packet2->size(), packet2->type(), @@ -170,7 +203,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet3->packet_number(), Thread::get_hrtime(), packet3->is_ack_eliciting(), - packet3->is_crypto_packet(), + false, true, packet3->size(), packet3->type(), @@ -179,7 +212,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet4->packet_number(), Thread::get_hrtime(), packet4->is_ack_eliciting(), - packet4->is_crypto_packet(), + false, true, packet4->size(), packet4->type(), @@ -188,7 +221,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet5->packet_number(), Thread::get_hrtime(), packet5->is_ack_eliciting(), - packet5->is_crypto_packet(), + false, true, packet5->size(), packet5->type(), @@ -197,7 +230,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet6->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet6->is_crypto_packet(), + false, true, packet6->size(), packet6->type(), @@ -206,7 +239,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet7->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet7->is_crypto_packet(), + false, true, packet7->size(), packet7->type(), @@ -215,7 +248,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet8->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet8->is_crypto_packet(), + false, true, packet8->size(), packet8->type(), @@ -224,7 +257,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet9->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet9->is_crypto_packet(), + false, true, packet9->size(), packet9->type(), @@ -233,7 +266,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet10->packet_number(), Thread::get_hrtime(), packet10->is_ack_eliciting(), - packet10->is_crypto_packet(), + false, true, packet10->size(), packet10->type(), diff --git a/iocore/net/quic/test/test_QUICPacket.cc b/iocore/net/quic/test/test_QUICPacket.cc index 573e1a89077..5dda30a6016 100644 --- a/iocore/net/quic/test/test_QUICPacket.cc +++ b/iocore/net/quic/test/test_QUICPacket.cc @@ -25,11 +25,18 @@ #include "quic/QUICPacket.h" -TEST_CASE("QUICPacketHeader - Long", "[quic]") +TEST_CASE("Receiving Packet", "[quic]") { - SECTION("Long Header (load) Version Negotiation Packet") + const uint8_t raw_dcid[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + }; + QUICConnectionId dcid(raw_dcid, sizeof(raw_dcid)); + + SECTION("Version Negotiation Packet") { - const uint8_t input[] = { + uint8_t input[] = { 0xc0, // Long header, Type: NONE 0x00, 0x00, 0x00, 0x00, // Version 0x08, // DCID Len @@ -39,23 +46,20 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x00, 0x00, 0x00, 0x08, // Supported Version 1 0x00, 0x00, 0x00, 0x09, // Supported Version 1 }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == sizeof(input) - 8); - CHECK(header->packet_size() == sizeof(input)); - CHECK(header->type() == QUICPacketType::VERSION_NEGOTIATION); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x00000000); + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICVersionNegotiationPacketR packet(nullptr, {}, {}, input_ibb); + CHECK(packet.type() == QUICPacketType::VERSION_NEGOTIATION); + CHECK(packet.size() == sizeof(input)); + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + CHECK(packet.version() == 0x00000000); } - SECTION("Long Header (load) INITIAL Packet") + SECTION("INITIAL Packet") { - const uint8_t input[] = { + uint8_t input[] = { 0xc3, // Long header, Type: INITIAL 0x11, 0x22, 0x33, 0x44, // Version 0x08, // DCID Len @@ -67,60 +71,54 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x23, 0x45, 0x67, // Packet number 0xff, 0xff, // Payload (dummy) }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == sizeof(input) - 2); // Packet Length - Payload Length - CHECK(header->packet_size() == sizeof(input)); - CHECK(header->type() == QUICPacketType::INITIAL); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICInitialPacketR packet(nullptr, {}, {}, input_ibb, 0); + CHECK(packet.type() == QUICPacketType::INITIAL); + CHECK(packet.size() == sizeof(input)); // Packet Length - Payload Length + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + CHECK(packet.packet_number() == 0x01234567); + CHECK(packet.version() == 0x11223344); } - SECTION("Long Header (load) RETRY Packet") + SECTION("RETRY Packet") { - const uint8_t input[] = { + uint8_t input[] = { 0xf5, // Long header, Type: RETRY 0x11, 0x22, 0x33, 0x44, // Version 0x08, // DCID Len 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID 0x08, // SCID Len 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID - 0x08, // ODCID Len - 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Original Destination Connection ID - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, // Retry Token - 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 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, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Retry Integrity Tag + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - const uint8_t retry_token[] = {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}; - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == sizeof(input) - 16); // Packet Length - Payload Length (Retry Token) - CHECK(header->packet_size() == sizeof(input)); - CHECK(header->type() == QUICPacketType::RETRY); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - - QUICPacketLongHeader *retry_header = static_cast(header.get()); - CHECK((retry_header->original_dcid() == - QUICConnectionId(reinterpret_cast("\x08\x07\x06\x05\x04\x03\x02\x01"), 8))); - - CHECK(memcmp(header->payload(), retry_token, 16) == 0); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(input, sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + const uint8_t retry_token[] = { + 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0xf0, 0xf1, 0xf2, + }; + + QUICRetryPacketR packet(nullptr, {}, {}, input_ibb); + CHECK(packet.type() == QUICPacketType::RETRY); + CHECK(packet.size() == sizeof(input)); + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + + CHECK(memcmp(packet.token().buf(), retry_token, 24) == 0); + CHECK(packet.version() == 0x11223344); } - SECTION("Long Header (parse) INITIAL Packet") + SECTION("INITIAL Packet") { - const uint8_t buf[] = { + uint8_t input[] = { 0xc3, // Long header, Type: INITIAL 0x11, 0x22, 0x33, 0x44, // Version 0x08, // DCID Len @@ -132,49 +130,90 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x23, 0x45, 0x67, // Packet number 0xff, 0xff, // Payload (dummy) }; + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(input, sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); - QUICPacketType type; - REQUIRE(QUICPacketLongHeader::type(type, buf, sizeof(buf))); - CHECK(type == QUICPacketType::INITIAL); - - QUICVersion version; - REQUIRE(QUICPacketLongHeader::version(version, buf, sizeof(buf))); - CHECK(version == 0x11223344); - - uint8_t dcil; - REQUIRE(QUICPacketLongHeader::dcil(dcil, buf, sizeof(buf))); - CHECK(dcil == 8); + QUICInitialPacketR packet(nullptr, {}, {}, input_ibb, 0); - uint8_t scil; - REQUIRE(QUICPacketLongHeader::scil(scil, buf, sizeof(buf))); - CHECK(dcil == 8); + CHECK(packet.type() == QUICPacketType::INITIAL); + CHECK(packet.size() == sizeof(input)); + CHECK(packet.version() == 0x11223344); + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + CHECK(packet.token().length() == 0); size_t token_length; uint8_t token_length_field_len; size_t token_length_field_offset; - REQUIRE(QUICPacketLongHeader::token_length(token_length, token_length_field_len, token_length_field_offset, buf, sizeof(buf))); + CHECK(QUICInitialPacketR::token_length(token_length, token_length_field_len, token_length_field_offset, input, sizeof(input))); CHECK(token_length == 0); CHECK(token_length_field_len == 1); CHECK(token_length_field_offset == 23); + } - size_t length; - uint8_t length_field_len; - size_t length_field_offset; - REQUIRE(QUICPacketLongHeader::length(length, length_field_len, length_field_offset, buf, sizeof(buf))); - CHECK(length == 6); - CHECK(length_field_len == 1); - CHECK(length_field_offset == 24); - - size_t pn_offset; - REQUIRE(QUICPacketLongHeader::packet_number_offset(pn_offset, buf, sizeof(buf))); - CHECK(pn_offset == 25); - - size_t packet_length; - REQUIRE(QUICPacketLongHeader::packet_length(packet_length, buf, sizeof(buf))); - CHECK(packet_length == sizeof(buf)); + SECTION("Short Header Packet") + { + uint8_t input[] = { + 0x43, // Short header with (K=0) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + 0x01, 0x23, 0x45, 0x67, // Packet number + 0xff, 0xff, // Payload (dummy) + }; + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICShortHeaderPacketR packet(nullptr, {}, {}, input_ibb, 0); + CHECK(packet.size() == 25); + CHECK(packet.key_phase() == QUICKeyPhase::PHASE_0); + CHECK(packet.destination_cid() == dcid); + CHECK(packet.packet_number() == 0x01234567); } +} - SECTION("Long Header (store) INITIAL Packet") +TEST_CASE("Sending Packet", "[quic]") +{ + const uint8_t raw_dcid[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + }; + QUICConnectionId dcid(raw_dcid, sizeof(raw_dcid)); + + SECTION("Short Header Packet (store)") + { + uint8_t buf[32] = {0}; + size_t len = 0; + + const uint8_t expected[] = { + 0x43, // Short header with (K=0) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + 0x01, 0x23, 0x45, 0x67, // Packet number + 0x11, 0x22, 0x33, 0x44, 0x55, // Protected Payload + }; + size_t payload_len = 5; + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(5, BUFFER_SIZE_INDEX_32K)); + payload->fill(5); + memcpy(payload->start(), expected + sizeof(expected) - payload_len, payload_len); + + QUICShortHeaderPacket packet(dcid, 0x1234567, 0, QUICKeyPhase::PHASE_0, true, true); + packet.attach_payload(payload, true); + + CHECK(packet.size() - 16 == 28); + CHECK(packet.key_phase() == QUICKeyPhase::PHASE_0); + CHECK(packet.type() == QUICPacketType::PROTECTED); + CHECK(packet.destination_cid() == dcid); + CHECK(packet.packet_number() == 0x01234567); + + packet.store(buf, &len); + CHECK(len == sizeof(expected)); + CHECK(memcmp(buf, expected, sizeof(expected)) == 0); + } + SECTION("INITIAL Packet (store)") { uint8_t buf[64] = {0}; size_t len = 0; @@ -191,31 +230,30 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x23, 0x45, 0x67, // Packet number 0x11, 0x22, 0x33, 0x44, 0x55, // Payload (dummy) }; - ats_unique_buf payload = ats_unique_malloc(5); - memcpy(payload.get(), expected + 17, 5); - - QUICPacketHeaderUPtr header = QUICPacketHeader::build( - QUICPacketType::INITIAL, QUICKeyPhase::INITIAL, {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, - {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, 0x01234567, 0, 0x11223344, true, - std::move(payload), 5); - - CHECK(header->size() == sizeof(expected) - 5); - CHECK(header->packet_size() == sizeof(expected)); - CHECK(header->type() == QUICPacketType::INITIAL); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); - CHECK(header->is_crypto_packet()); - - header->store(buf, &len); - CHECK(len == header->size()); - CHECK(memcmp(buf, expected, len) == 0); + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(5, BUFFER_SIZE_INDEX_32K)); + payload->fill(5); + memcpy(payload->start(), expected + 17, 5); + + QUICInitialPacket packet(0x11223344, {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, + {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, 0, nullptr, 5, 0x01234567, + true, true, true); + packet.attach_payload(payload, true); + + CHECK(packet.size() == sizeof(expected) + 16); + CHECK(packet.type() == QUICPacketType::INITIAL); + CHECK((packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); + CHECK((packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); + CHECK(packet.packet_number() == 0x01234567); + CHECK(packet.version() == 0x11223344); + CHECK(packet.is_crypto_packet()); + + packet.store(buf, &len); + CHECK(len == packet.size() - 16); + CHECK(memcmp(buf, expected, len - 16) == 0); } - SECTION("Long Header (store) RETRY Packet") + SECTION("RETRY Packet (store)") { uint8_t buf[64] = {0}; size_t len = 0; @@ -227,104 +265,25 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID 0x08, // SCID Len 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID - 0x08, // ODCID Len - 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Original Destination Connection ID - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, // Retry Token - 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, - }; - ats_unique_buf payload = ats_unique_malloc(16); - memcpy(payload.get(), expected + 30, 16); - - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::RETRY, QUICKeyPhase::INITIAL, 0x11223344, - {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, - {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, - {reinterpret_cast("\x08\x07\x06\x05\x04\x03\x02\x01"), 8}, std::move(payload), 16); - - CHECK(header->size() == sizeof(expected) - 16); - CHECK(header->packet_size() == sizeof(expected)); - CHECK(header->type() == QUICPacketType::RETRY); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); - - QUICPacketLongHeader *retry_header = static_cast(header.get()); - CHECK((retry_header->original_dcid() == - QUICConnectionId(reinterpret_cast("\x08\x07\x06\x05\x04\x03\x02\x01"), 8))); - - header->store(buf, &len); - CHECK(len == header->size()); - CHECK(memcmp(buf, expected, 22) == 0); - CHECK(memcmp(buf + 22, expected + 22, 8) == 0); - } -} - -TEST_CASE("QUICPacketHeader - Short", "[quic]") -{ - const uint8_t raw_dcid[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // - 0x10, 0x11, // - }; - QUICConnectionId dcid(raw_dcid, sizeof(raw_dcid)); - - SECTION("Short Header (load)") - { - const uint8_t input[] = { - 0x43, // Short header with (K=0) - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Retry Token 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // - 0x10, 0x11, // - 0x01, 0x23, 0x45, 0x67, // Packet number - 0xff, 0xff, // Payload (dummy) + 0x10, 0x11, 0x12, 0x13, 0x14, 0xf0, 0xf1, 0xf2, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Retry Integrity Tag + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == 23); - CHECK(header->packet_size() == 25); - CHECK(header->key_phase() == QUICKeyPhase::PHASE_0); - CHECK(header->destination_cid() == dcid); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == false); - } - - SECTION("Short Header (store)") - { - uint8_t buf[32] = {0}; - size_t len = 0; - - const uint8_t expected[] = { - 0x43, // Short header with (K=0) - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // - 0x10, 0x11, // - 0x01, 0x23, 0x45, 0x67, // Packet number - 0x11, 0x22, 0x33, 0x44, 0x55, // Protected Payload - }; - size_t payload_len = 5; - size_t header_len = sizeof(expected) - 5; - - ats_unique_buf payload = ats_unique_malloc(payload_len); - memcpy(payload.get(), expected + header_len, payload_len); - - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::PROTECTED, QUICKeyPhase::PHASE_0, dcid, 0x01234567, 0, std::move(payload), 32); - - CHECK(header->size() == 23); - CHECK(header->packet_size() == 0); - CHECK(header->key_phase() == QUICKeyPhase::PHASE_0); - CHECK(header->type() == QUICPacketType::PROTECTED); - CHECK(header->destination_cid() == dcid); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == false); - - header->store(buf, &len); - CHECK(len == header_len); - CHECK(memcmp(buf, expected, header_len) == 0); + QUICRetryToken token(expected + 23, 24); + + 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); + CHECK(packet.size() == sizeof(expected)); + CHECK(packet.type() == QUICPacketType::RETRY); + CHECK((packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); + CHECK((packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); + CHECK(packet.version() == 0x11223344); + + packet.store(buf, &len); + CHECK(len == packet.size()); + CHECK(memcmp(buf, expected, sizeof(expected) - 16) == 0); } } @@ -354,3 +313,379 @@ TEST_CASE("Decoding Packet Number 1", "[quic]") QUICPacket::decode_packet_number(dst, src, len, base); CHECK(dst == 0xaa8309b3); } + +TEST_CASE("read_essential_info", "[quic]") +{ + SECTION("Long header packet - INITIAL") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0x00, // Token Length (i), Token (*) + 0x06, // Length + 0x01, 0x23, 0x45, 0x67, // Packet number + 0xff, 0xff, // Payload (dummy) + }; + + QUICConnectionId expected_dcid(input + 6, 8); + QUICConnectionId expected_scid(input + 15, 8); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::INITIAL); + CHECK(version == 0x11223344); + CHECK(dcid == expected_dcid); + CHECK(scid == expected_scid); + CHECK(packet_number == 0x01234567); + } + + SECTION("Long header packet - INITIAL - 0 length CID") + { + uint8_t input[] = { + 0xc2, // Long header, Type: INITIAL + 0xff, 0x00, 0x00, 0x19, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x00, // SCID Len + 0x00, // Token Length (i), Token (*) + 0x42, 0x17, // Length + 0x00, 0x00, 0x00 // Packet number + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + } + + SECTION("Long header packet - RETRY") + { + uint8_t input[] = { + 0xf0, // Long header, Type: RETRY + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 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, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Retry Integrity Tag + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + QUICConnectionId expected_dcid(input + 6, 8); + QUICConnectionId expected_scid(input + 15, 8); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::RETRY); + CHECK(version == 0x11223344); + CHECK(dcid == expected_dcid); + CHECK(scid == expected_scid); + } + + SECTION("Long header packet - Version Negotiation") + { + uint8_t input[] = { + 0xd9, // Long header, Type: RETRY + 0x00, 0x00, 0x00, 0x00, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0xff, 0x00, 0x00, 0x19, // Supported Version 1 + 0xa1, 0xa2, 0xa3, 0xa4, // Supported Version 2 + }; + + QUICConnectionId expected_dcid(input + 6, 8); + QUICConnectionId expected_scid(input + 15, 8); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::VERSION_NEGOTIATION); + CHECK(version == 0x00); + CHECK(dcid == expected_dcid); + CHECK(scid == expected_scid); + } + + SECTION("Short header packet") + { + uint8_t input[] = { + 0x43, // Short header with (K=0) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + 0x01, 0x23, 0x45, 0x67, // Packet number + 0xff, 0xff, // Payload (dummy) + }; + + QUICConnectionId expected_dcid(input + 1, 18); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::PROTECTED); + CHECK(key_phase == QUICKeyPhase::PHASE_0); + CHECK(dcid == expected_dcid); + CHECK(packet_number == 0x01234567); + } + + SECTION("Long header packet - Malformed INITIAL 1") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 2") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, // Version + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 3") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 4") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 5") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, // Destination Connection ID + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 6") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 7") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + SECTION("Long header packet - Malformed INITIAL 8") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0x80, // Token Length (i), Token (*) + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 9") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0x00, // Token Length (i), Token (*) + 0x06, // Length + 0x01, 0x23, // Packet number + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } +} diff --git a/iocore/net/quic/test/test_QUICPacketFactory.cc b/iocore/net/quic/test/test_QUICPacketFactory.cc index dc017658803..39c2c43e29d 100644 --- a/iocore/net/quic/test/test_QUICPacketFactory.cc +++ b/iocore/net/quic/test/test_QUICPacketFactory.cc @@ -24,6 +24,7 @@ #include "catch.hpp" #include "quic/QUICPacket.h" +#include "quic/QUICPacketFactory.h" #include "quic/Mock.h" TEST_CASE("QUICPacketFactory_Create_VersionNegotiationPacket", "[quic]") @@ -36,15 +37,16 @@ TEST_CASE("QUICPacketFactory_Create_VersionNegotiationPacket", "[quic]") QUICConnectionId dcid(raw_dcid, 8); QUICConnectionId scid(raw_scid, 8); - QUICPacketUPtr vn_packet = factory.create_version_negotiation_packet(scid, dcid); + QUICPacketUPtr packet = factory.create_version_negotiation_packet(scid, dcid); + REQUIRE(packet != nullptr); - REQUIRE(vn_packet != nullptr); - CHECK(vn_packet->type() == QUICPacketType::VERSION_NEGOTIATION); - CHECK(vn_packet->destination_cid() == scid); - CHECK(vn_packet->source_cid() == dcid); - CHECK(vn_packet->version() == 0x00); + QUICVersionNegotiationPacket &vn_packet = static_cast(*packet); + CHECK(vn_packet.type() == QUICPacketType::VERSION_NEGOTIATION); + CHECK(vn_packet.destination_cid() == scid); + CHECK(vn_packet.source_cid() == dcid); + CHECK(vn_packet.version() == 0x00); - QUICVersion supported_version = QUICTypeUtil::read_QUICVersion(vn_packet->payload()); + QUICVersion supported_version = QUICTypeUtil::read_QUICVersion(reinterpret_cast(vn_packet.payload_block()->start())); CHECK(supported_version == QUIC_SUPPORTED_VERSIONS[0]); uint8_t expected[] = { @@ -54,12 +56,12 @@ 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, 0x17, // Supported Version + 0xff, 0x00, 0x00, 0x1b, // Supported Version 0x1a, 0x2a, 0x3a, 0x4a, // Excercise Version }; uint8_t buf[1024] = {0}; size_t buf_len; - vn_packet->store(buf, &buf_len); + vn_packet.store(buf, &buf_len); CHECK((buf[0] & 0x80) == 0x80); // Lower 7 bits of the first byte is random CHECK(memcmp(buf + 1, expected + 1, buf_len - 1) == 0); } @@ -75,15 +77,15 @@ TEST_CASE("QUICPacketFactory_Create_Retry", "[quic]") QUICPacketUPtr packet = factory.create_retry_packet(QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), - QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), - QUICConnectionId(reinterpret_cast("\x04\x03\x02\x01"), 4), token); + QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), token); REQUIRE(packet != nullptr); - CHECK(packet->type() == QUICPacketType::RETRY); - CHECK((packet->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4))); - CHECK(memcmp(packet->payload(), raw, sizeof(raw)) == 0); - CHECK(packet->packet_number() == 0); - CHECK(packet->version() == QUIC_SUPPORTED_VERSIONS[0]); + + QUICRetryPacket &retry_packet = static_cast(*packet); + CHECK(retry_packet.type() == QUICPacketType::RETRY); + CHECK(retry_packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4)); + CHECK(retry_packet.version() == QUIC_SUPPORTED_VERSIONS[0]); + CHECK(retry_packet.token() == token); } TEST_CASE("QUICPacketFactory_Create_Handshake", "[quic]") @@ -93,20 +95,24 @@ TEST_CASE("QUICPacketFactory_Create_Handshake", "[quic]") QUICPacketFactory factory(pp_key_info); factory.set_version(0x11223344); - uint8_t raw[] = {0xaa, 0xbb, 0xcc, 0xdd}; - ats_unique_buf payload = ats_unique_malloc(sizeof(raw)); - memcpy(payload.get(), raw, sizeof(raw)); + uint8_t raw[] = {0xaa, 0xbb, 0xcc, 0xdd}; + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(sizeof(raw), BUFFER_SIZE_INDEX_32K)); + payload->fill(sizeof(raw)); + memcpy(payload->start(), raw, sizeof(raw)); - QUICPacketUPtr packet = - factory.create_handshake_packet(QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), - QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), 0, - std::move(payload), sizeof(raw), true, false, true); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = factory.create_handshake_packet( + packet_buf, QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), + QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), 0, payload, sizeof(raw), true, false, true); REQUIRE(packet != nullptr); - CHECK(packet->type() == QUICPacketType::HANDSHAKE); - CHECK((packet->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4))); - CHECK(memcmp(packet->payload(), raw, sizeof(raw)) != 0); - CHECK(packet->packet_number() <= 0xFFFFFBFF); - CHECK(packet->version() == 0x11223344); + + QUICHandshakePacket &handshake_packet = reinterpret_cast(*packet); + CHECK(handshake_packet.type() == QUICPacketType::HANDSHAKE); + CHECK(handshake_packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4)); + CHECK(memcmp(handshake_packet.payload_block()->start(), raw, sizeof(raw)) != 0); + CHECK(handshake_packet.packet_number() <= 0xFFFFFBFF); + CHECK(handshake_packet.version() == 0x11223344); } TEST_CASE("QUICPacketFactory_Create_StatelessResetPacket", "[quic]") @@ -115,10 +121,11 @@ TEST_CASE("QUICPacketFactory_Create_StatelessResetPacket", "[quic]") QUICPacketFactory factory(pp_key_info); QUICStatelessResetToken token({reinterpret_cast("\x30\x39"), 2}, 67890); - QUICPacketUPtr packet = - factory.create_stateless_reset_packet(QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), token); + QUICPacketUPtr packet = factory.create_stateless_reset_packet(token, 1200); REQUIRE(packet != nullptr); CHECK(packet->type() == QUICPacketType::STATELESS_RESET); - CHECK((packet->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4))); + + QUICStatelessResetPacket *sr_packet = dynamic_cast(packet.get()); + CHECK(sr_packet->token() == token); } diff --git a/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc b/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc index 5cb69af9efa..031aa51cd37 100644 --- a/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc +++ b/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc @@ -32,9 +32,6 @@ struct PollCont; #include "P_UnixNet.h" #include "P_UnixNetVConnection.h" -// depends on size of cert -static constexpr uint32_t MAX_HANDSHAKE_MSG_LEN = 8192; - #include "./server_cert.h" TEST_CASE("QUICPacketHeaderProtector") @@ -127,6 +124,13 @@ 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); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + 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})); @@ -135,41 +139,37 @@ TEST_CASE("QUICPacketHeaderProtector") // Handshake // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; - REQUIRE(client->handshake(&msg1, nullptr) == 1); + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); // FIN - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg3 = nullptr; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg2_1; uint8_t msg2_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg2_1.buf = msg2_1_buf; msg2_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg2_1.buf, msg2.buf, msg2.offsets[1]); + memcpy(msg2_1.buf, msg2->buf, msg2->offsets[1]); msg2_1.offsets[0] = 0; - msg2_1.offsets[1] = msg2.offsets[1]; - msg2_1.offsets[2] = msg2.offsets[1]; - msg2_1.offsets[3] = msg2.offsets[1]; - msg2_1.offsets[4] = msg2.offsets[1]; + msg2_1.offsets[1] = msg2->offsets[1]; + msg2_1.offsets[2] = msg2->offsets[1]; + msg2_1.offsets[3] = msg2->offsets[1]; + msg2_1.offsets[4] = msg2->offsets[1]; // EE - FIN QUICHandshakeMsgs msg2_2; @@ -177,8 +177,8 @@ TEST_CASE("QUICPacketHeaderProtector") msg2_2.buf = msg2_2_buf; msg2_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg2.offsets[3] - msg2.offsets[2]; - memcpy(msg2_2.buf, msg2.buf + msg2.offsets[1], len); + size_t len = msg2->offsets[3] - msg2->offsets[2]; + memcpy(msg2_2.buf, msg2->buf + msg2->offsets[1], len); msg2_2.offsets[0] = 0; msg2_2.offsets[1] = 0; msg2_2.offsets[2] = 0; @@ -187,23 +187,16 @@ TEST_CASE("QUICPacketHeaderProtector") REQUIRE(client->handshake(&msg3, &msg2_1) == 1); REQUIRE(client->handshake(&msg3, &msg2_2) == 1); -#else - REQUIRE(client->handshake(&msg3, &msg2) == 1); -#endif + REQUIRE(msg3); // NS - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); - - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - REQUIRE(client->handshake(&msg5, &msg4) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); + + QUICHandshakeMsgs *msg5 = nullptr; + REQUIRE(client->handshake(&msg5, msg4) == 1); + REQUIRE(msg5 == nullptr); // ## Client -> Server REQUIRE(client_ph_protector.protect(tmp, sizeof(tmp), 18)); diff --git a/iocore/net/quic/test/test_QUICPathValidator.cc b/iocore/net/quic/test/test_QUICPathValidator.cc index 78c51f2f355..cd96ef74a2b 100644 --- a/iocore/net/quic/test/test_QUICPathValidator.cc +++ b/iocore/net/quic/test/test_QUICPathValidator.cc @@ -95,7 +95,7 @@ TEST_CASE("QUICPathValidator", "[quic]") memcpy(buf + len, b->start(), b->size()); len += b->size(); } - MockQUICPacket mock_packet; + MockQUICPacketR mock_packet; auto received_frame = QUICFrameFactory::create(received_frame_buf, buf, len, &mock_packet); mock_packet.set_from(remote); mock_packet.set_to(local); diff --git a/iocore/net/quic/test/test_QUICPinger.cc b/iocore/net/quic/test/test_QUICPinger.cc index fce04c62ce1..bb257026c78 100644 --- a/iocore/net/quic/test/test_QUICPinger.cc +++ b/iocore/net/quic/test/test_QUICPinger.cc @@ -33,69 +33,69 @@ TEST_CASE("QUICPinger", "[quic]") SECTION("request and cancel") { QUICPinger pinger; - pinger.request(); - REQUIRE(pinger.count() == 1); - pinger.request(); - REQUIRE(pinger.count() == 2); - pinger.cancel(); - REQUIRE(pinger.count() == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 2); + pinger.cancel(level); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.generate_frame(frame, level, UINT64_MAX, UINT16_MAX, 0, 0) != nullptr); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); } SECTION("generate PING Frame twice") { QUICPinger pinger; - pinger.request(); - REQUIRE(pinger.count() == 1); - pinger.request(); - REQUIRE(pinger.count() == 2); + pinger.request(level); + REQUIRE(pinger.count(level) == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 2); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 0) == true); - REQUIRE(pinger.count() == 2); + REQUIRE(pinger.count(level) == 2); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 0) == false); - REQUIRE(pinger.count() == 2); + REQUIRE(pinger.count(level) == 2); } SECTION("don't generate frame when packet is ack_elicting") { QUICPinger pinger; - pinger.request(); - REQUIRE(pinger.count() == 1); - pinger.request(); - REQUIRE(pinger.count() == 2); + pinger.request(level); + REQUIRE(pinger.count(level) == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 2); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 0) == false); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 1) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); } SECTION("generating PING Frame for next continuos un-ack-eliciting packets") { QUICPinger pinger; REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 0) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 1) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 2) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 3) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); } SECTION("don't send PING Frame for empty packet") { QUICPinger pinger; REQUIRE(pinger.will_generate_frame(level, 0, false, 0) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 1) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 2) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 3) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, 0, false, 4) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, 1, false, 5) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); } } diff --git a/iocore/net/quic/test/test_QUICStream.cc b/iocore/net/quic/test/test_QUICStream.cc index 1ed23562515..5527fdd691c 100644 --- a/iocore/net/quic/test/test_QUICStream.cc +++ b/iocore/net/quic/test/test_QUICStream.cc @@ -33,7 +33,7 @@ TEST_CASE("QUICBidiStream", "[quic]") uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; uint32_t stream_id = 0x03; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), payload, sizeof(payload)); block->fill(sizeof(payload)); @@ -178,7 +178,7 @@ TEST_CASE("QUICBidiStream", "[quic]") stream->do_io_read(nullptr, INT64_MAX, read_buffer); Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); block->fill(1024); // Start with 1024 but not 0 so received frames won't be processed @@ -421,7 +421,7 @@ TEST_CASE("QUIC receive only stream", "[quic]") uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; uint32_t stream_id = 0x03; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), payload, sizeof(payload)); block->fill(sizeof(payload)); @@ -562,7 +562,7 @@ TEST_CASE("QUIC receive only stream", "[quic]") stream->do_io_read(nullptr, INT64_MAX, read_buffer); Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); block->fill(1024); // Start with 1024 but not 0 so received frames won't be processed @@ -619,7 +619,7 @@ TEST_CASE("QUIC send only stream", "[quic]") uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; uint32_t stream_id = 0x03; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), payload, sizeof(payload)); block->fill(sizeof(payload)); @@ -672,7 +672,6 @@ TEST_CASE("QUIC send only stream", "[quic]") MIOBuffer *write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_4K); IOBufferReader *write_buffer_reader = write_buffer->alloc_reader(); - QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, 4096)); SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); @@ -766,7 +765,6 @@ TEST_CASE("QUIC send only stream", "[quic]") MIOBuffer *write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_8K); IOBufferReader *write_buffer_reader = write_buffer->alloc_reader(); - QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); @@ -814,7 +812,6 @@ TEST_CASE("QUIC send only stream", "[quic]") MIOBuffer *write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_8K); IOBufferReader *write_buffer_reader = write_buffer->alloc_reader(); - QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); diff --git a/iocore/net/quic/test/test_QUICStreamManager.cc b/iocore/net/quic/test/test_QUICStreamManager.cc index b875c79aa98..8d7ddf28bfc 100644 --- a/iocore/net/quic/test/test_QUICStreamManager.cc +++ b/iocore/net/quic/test/test_QUICStreamManager.cc @@ -29,6 +29,8 @@ #include "quic/QUICFrame.h" #include "quic/Mock.h" +MockQUICContext context; + TEST_CASE("QUICStreamManager_NewStream", "[quic]") { QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; @@ -37,23 +39,20 @@ TEST_CASE("QUICStreamManager_NewStream", "[quic]") MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); MockQUICConnectionInfoProvider cinfo_provider; - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(&cinfo_provider, &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); uint8_t local_tp_buf[] = { - 0x00, 0x06, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value - 0x40, 0x10 // value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value + 0x40, 0x10 // value }; std::shared_ptr local_tp = std::make_shared(local_tp_buf, sizeof(local_tp_buf)); uint8_t remote_tp_buf[] = { - 0x00, 0x06, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value - 0x40, 0x10 // value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value + 0x40, 0x10 // value }; std::shared_ptr remote_tp = std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); @@ -94,13 +93,6 @@ TEST_CASE("QUICStreamManager_NewStream", "[quic]") QUICFrame *stream_blocked_frame = QUICFrameFactory::create_stream_data_blocked_frame(stream_blocked_frame_buf, 0x10, 0); sm.handle_frame(level, *stream_blocked_frame); CHECK(sm.stream_count() == 5); - - // Set local maximum stream id - sm.set_max_streams_bidi(5); - uint8_t stream_blocked_frame_x_buf[QUICFrame::MAX_INSTANCE_SIZE]; - QUICFrame *stream_blocked_frame_x = QUICFrameFactory::create_stream_data_blocked_frame(stream_blocked_frame_x_buf, 0x18, 0); - sm.handle_frame(level, *stream_blocked_frame_x); - CHECK(sm.stream_count() == 5); } TEST_CASE("QUICStreamManager_first_initial_map", "[quic]") @@ -111,8 +103,7 @@ TEST_CASE("QUICStreamManager_first_initial_map", "[quic]") MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); MockQUICConnectionInfoProvider cinfo_provider; - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(&cinfo_provider, &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); std::shared_ptr local_tp = std::make_shared(); std::shared_ptr remote_tp = std::make_shared(); sm.init_flow_control_params(local_tp, remote_tp); @@ -137,28 +128,25 @@ TEST_CASE("QUICStreamManager_total_offset_received", "[quic]") MockQUICConnection connection; MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(new MockQUICConnectionInfoProvider(), &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); uint8_t local_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x05, // parameter id - initial_max_stream_data_bidi_local - 0x00, 0x04, // length of value + 0x05, // parameter id - initial_max_stream_data_bidi_local + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr local_tp = std::make_shared(local_tp_buf, sizeof(local_tp_buf)); uint8_t remote_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x06, // parameter id - initial_max_stream_data_bidi_remote - 0x00, 0x04, // length of value + 0x06, // parameter id - initial_max_stream_data_bidi_remote + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr remote_tp = @@ -195,28 +183,25 @@ TEST_CASE("QUICStreamManager_total_offset_sent", "[quic]") MockQUICConnection connection; MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(new MockQUICConnectionInfoProvider(), &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); uint8_t local_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x05, // parameter id - initial_max_stream_data_bidi_local - 0x00, 0x04, // length of value + 0x05, // parameter id - initial_max_stream_data_bidi_local + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr local_tp = std::make_shared(local_tp_buf, sizeof(local_tp_buf)); uint8_t remote_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x06, // parameter id - initial_max_stream_data_bidi_remote - 0x00, 0x04, // length of value + 0x06, // parameter id - initial_max_stream_data_bidi_remote + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr remote_tp = @@ -258,3 +243,124 @@ TEST_CASE("QUICStreamManager_total_offset_sent", "[quic]") // Wait for event processing sleep(2); } + +TEST_CASE("QUICStreamManager_max_streams", "[quic]") +{ + QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; + QUICApplicationMap app_map; + MockQUICConnection connection; + MockQUICApplication mock_app(&connection); + app_map.set_default(&mock_app); + QUICStreamManager sm(&context, &app_map); + + uint8_t local_tp_buf[] = { + 0x08, // parameter id - initial_max_streams_bidi + 0x01, // length of value + 0x03, // value + 0x09, // parameter id - initial_max_streams_uni + 0x01, // length of value + 0x03, // value + }; + std::shared_ptr local_tp = + std::make_shared(local_tp_buf, sizeof(local_tp_buf)); + + uint8_t remote_tp_buf[] = { + 0x08, // parameter id - initial_max_streams_bidi + 0x01, // length of value + 0x03, // value + 0x09, // parameter id - initial_max_streams_uni + 0x01, // length of value + 0x03, // value + }; + std::shared_ptr remote_tp = + std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); + + sm.init_flow_control_params(local_tp, remote_tp); + + SECTION("local") + { + // RESET_STREAM frames create new streams + + // Bidirectional + uint8_t rst_stream_frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + QUICFrame *rst_stream_frame = + QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 1, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 1); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 5, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 2); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 9, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 3); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 13, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 3); + + // Unidirectional + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 3, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 4); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 7, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 5); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 11, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 6); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 15, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 6); + } + + SECTION("remote") + { + // Bidirection + QUICConnectionErrorUPtr error; + QUICStreamId id; + + error = sm.create_bidi_stream(id); + REQUIRE(!error); + REQUIRE(id == 0); + REQUIRE(sm.stream_count() == 1); + + error = sm.create_bidi_stream(id); + REQUIRE(!error); + REQUIRE(id == 4); + REQUIRE(sm.stream_count() == 2); + + error = sm.create_bidi_stream(id); + REQUIRE(!error); + REQUIRE(id == 8); + REQUIRE(sm.stream_count() == 3); + + error = sm.create_bidi_stream(id); + REQUIRE(error); + REQUIRE(sm.stream_count() == 3); + + // Unidirection + error = sm.create_uni_stream(id); + REQUIRE(!error); + REQUIRE(id == 2); + REQUIRE(sm.stream_count() == 4); + + error = sm.create_uni_stream(id); + REQUIRE(!error); + REQUIRE(id == 6); + REQUIRE(sm.stream_count() == 5); + + error = sm.create_uni_stream(id); + REQUIRE(!error); + REQUIRE(id == 10); + REQUIRE(sm.stream_count() == 6); + + error = sm.create_uni_stream(id); + REQUIRE(error); + REQUIRE(sm.stream_count() == 6); + } +} diff --git a/iocore/net/quic/test/test_QUICStreamState.cc b/iocore/net/quic/test/test_QUICStreamState.cc index b5538669d16..9eb11281475 100644 --- a/iocore/net/quic/test/test_QUICStreamState.cc +++ b/iocore/net/quic/test/test_QUICStreamState.cc @@ -33,7 +33,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") { Ptr block_4 = make_ptr(new_IOBufferBlock()); - block_4->alloc(); + block_4->alloc(BUFFER_SIZE_INDEX_32K); block_4->fill(4); CHECK(block_4->read_avail() == 4); @@ -183,7 +183,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") TEST_CASE("QUICReceiveStreamState", "[quic]") { Ptr block_4 = make_ptr(new_IOBufferBlock()); - block_4->alloc(); + block_4->alloc(BUFFER_SIZE_INDEX_32K); block_4->fill(4); CHECK(block_4->read_avail() == 4); @@ -366,7 +366,7 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") TEST_CASE("QUICBidiState", "[quic]") { Ptr block_4 = make_ptr(new_IOBufferBlock()); - block_4->alloc(); + block_4->alloc(BUFFER_SIZE_INDEX_32K); block_4->fill(4); CHECK(block_4->read_avail() == 4); diff --git a/iocore/net/quic/test/test_QUICTransportParameters.cc b/iocore/net/quic/test/test_QUICTransportParameters.cc index 95fb0ae27bd..a225b86fed7 100644 --- a/iocore/net/quic/test/test_QUICTransportParameters.cc +++ b/iocore/net/quic/test/test_QUICTransportParameters.cc @@ -30,18 +30,17 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") SECTION("OK") { uint8_t buf[] = { - 0x00, 0x1c, // size of parameters - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x11, 0x22, 0x33, 0x44, // value - 0x00, 0x01, // parameter id - 0x00, 0x04, // length of value + 0x01, // parameter id + 0x04, // length of value 0x12, 0x34, 0x56, 0x78, // value - 0x00, 0x05, // parameter id - 0x00, 0x02, // length of value + 0x05, // parameter id + 0x02, // length of value 0x0a, 0x0b, // value - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x05, 0x67, // value }; @@ -55,7 +54,7 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") CHECK(len == 4); CHECK(memcmp(data, "\x11\x22\x33\x44", 4) == 0); - data = params_in_ch.getAsBytes(QUICTransportParameterId::IDLE_TIMEOUT, len); + data = params_in_ch.getAsBytes(QUICTransportParameterId::MAX_IDLE_TIMEOUT, len); CHECK(len == 4); CHECK(memcmp(data, "\x12\x34\x56\x78", 4) == 0); @@ -75,12 +74,11 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") SECTION("Duplicate parameters") { uint8_t buf[] = { - 0x00, 0x10, // size of parameters - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x11, 0x22, 0x33, 0x44, // value - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x12, 0x34, 0x56, 0x78, // value }; @@ -95,16 +93,15 @@ TEST_CASE("QUICTransportParametersInClientHello_write", "[quic]") uint16_t len; uint8_t expected[] = { - 0x00, 0x22, // size of parameters - 0x00, 0x02, // parameter id - 0x00, 0x10, // length of value + 0x02, // parameter id + 0x10, // length of value 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // value 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // value - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x5b, 0xcd, // value - 0x00, 0x05, // parameter id - 0x00, 0x04, // length of value + 0x05, // parameter id + 0x04, // length of value 0x91, 0x22, 0x33, 0x44, // value }; @@ -121,7 +118,7 @@ TEST_CASE("QUICTransportParametersInClientHello_write", "[quic]") params_in_ch.set(QUICTransportParameterId::STATELESS_RESET_TOKEN, stateless_reset_token, 16); params_in_ch.store(buf, &len); - CHECK(len == 36); + CHECK(len == 28); CHECK(memcmp(buf, expected, len) == 0); } @@ -130,19 +127,19 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") SECTION("OK case") { uint8_t buf[] = { - 0x00, 0x2a, // size of parameters - 0x00, 0x01, // parameter id - 0x00, 0x02, // length of value - 0x51, 0x23, // value - 0x00, 0x02, // parameter id - 0x00, 0x10, // length of value - 0x00, 0x10, 0x20, 0x30, // value - 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x00, 0x04, // parameter id - 0x00, 0x04, // length of value - 0x92, 0x34, 0x56, 0x78, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value - 0x91, 0x22, 0x33, 0x44, // value + 0x01, // parameter id + 0x02, // length of value + 0x51, 0x23, // value + 0x02, // parameter id + 0x10, // length of value + 0x00, 0x10, 0x20, 0x30, // value + 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0x04, // parameter id + 0x04, // length of value + 0x92, 0x34, 0x56, 0x78, // value + 0x06, // parameter id + 0x04, // length of value + 0x91, 0x22, 0x33, 0x44, // value }; QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf)); @@ -159,13 +156,13 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") CHECK(len == 4); CHECK(memcmp(data, "\x92\x34\x56\x78", 4) == 0); - data = params_in_ee.getAsBytes(QUICTransportParameterId::IDLE_TIMEOUT, len); + data = params_in_ee.getAsBytes(QUICTransportParameterId::MAX_IDLE_TIMEOUT, len); CHECK(len == 2); CHECK(memcmp(data, "\x51\x23", 2) == 0); data = params_in_ee.getAsBytes(QUICTransportParameterId::STATELESS_RESET_TOKEN, len); CHECK(len == 16); - CHECK(memcmp(data, buf + 12, 16) == 0); + CHECK(memcmp(data, buf + 6, 16) == 0); CHECK(!params_in_ee.contains(QUICTransportParameterId::DISABLE_ACTIVE_MIGRATION)); } @@ -173,18 +170,17 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") SECTION("OK case - zero length value") { uint8_t buf[] = { - 0x00, 0x1a, // size of parameters - 0x00, 0x01, // parameter id - 0x00, 0x02, // length of value + 0x01, // parameter id + 0x02, // length of value 0x51, 0x23, // value - 0x00, 0x04, // parameter id - 0x00, 0x04, // length of value + 0x04, // parameter id + 0x04, // length of value 0xa2, 0x34, 0x56, 0x78, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value + 0x06, // parameter id + 0x04, // length of value 0xa1, 0x22, 0x33, 0x44, // value - 0x00, 0x0c, // parameter id - 0x00, 0x00, // length of value + 0x0c, // parameter id + 0x00, // length of value }; QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf)); @@ -201,7 +197,7 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") CHECK(len == 4); CHECK(memcmp(data, "\xa2\x34\x56\x78", 4) == 0); - data = params_in_ee.getAsBytes(QUICTransportParameterId::IDLE_TIMEOUT, len); + data = params_in_ee.getAsBytes(QUICTransportParameterId::MAX_IDLE_TIMEOUT, len); CHECK(len == 2); CHECK(memcmp(data, "\x51\x23", 2) == 0); @@ -211,12 +207,11 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") SECTION("Duplicate parameters") { uint8_t buf[] = { - 0x00, 0x1e, // size of parameters - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x01, 0x02, 0x03, 0x04, // value - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x12, 0x34, 0x56, 0x78, // value }; @@ -233,12 +228,11 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") uint16_t len; uint8_t expected[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x5b, 0xcd, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value + 0x06, // parameter id + 0x04, // length of value 0x91, 0x22, 0x33, 0x44, // value }; @@ -253,7 +247,7 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") params_in_ee.add_version(0x01020304); params_in_ee.add_version(0x05060708); params_in_ee.store(buf, &len); - CHECK(len == 16); + CHECK(len == 10); CHECK(memcmp(buf, expected, len) == 0); } @@ -263,15 +257,14 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") uint16_t len; uint8_t expected[] = { - 0x00, 0x12, // size of parameters - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x5b, 0xcd, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value + 0x06, // parameter id + 0x04, // length of value 0x91, 0x22, 0x33, 0x44, // value - 0x00, 0x0c, // parameter id - 0x00, 0x00, // length of value + 0x0c, // parameter id + 0x00, // length of value }; QUICTransportParametersInEncryptedExtensions params_in_ee; @@ -286,7 +279,7 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") params_in_ee.add_version(0x01020304); params_in_ee.add_version(0x05060708); params_in_ee.store(buf, &len); - CHECK(len == 20); + CHECK(len == 12); CHECK(memcmp(buf, expected, len) == 0); } } diff --git a/iocore/net/quic/test/test_QUICVersionNegotiator.cc b/iocore/net/quic/test/test_QUICVersionNegotiator.cc index be76949a89d..a1bcab3f3b0 100644 --- a/iocore/net/quic/test/test_QUICVersionNegotiator.cc +++ b/iocore/net/quic/test/test_QUICVersionNegotiator.cc @@ -25,6 +25,7 @@ #include "quic/QUICVersionNegotiator.h" #include "quic/QUICPacketProtectionKeyInfo.h" +#include "quic/QUICPacketFactory.h" #include "quic/Mock.h" TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") @@ -34,8 +35,11 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") QUICPacketFactory packet_factory(pp_key_info); QUICVersionNegotiator vn; - ats_unique_buf dummy_payload = ats_unique_malloc(128); - size_t dummy_payload_len = 128; + + size_t dummy_payload_len = 128; + Ptr dummy_payload = make_ptr(new_IOBufferBlock()); + dummy_payload->alloc(iobuffer_size_to_index(dummy_payload_len, BUFFER_SIZE_INDEX_32K)); + dummy_payload->fill(dummy_payload_len); SECTION("Normal case") { @@ -44,11 +48,15 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_SUPPORTED_VERSIONS[0]); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - + packet_factory.create_initial_packet(packet_buf, {}, {}, 0, dummy_payload, dummy_payload_len, true, false, true); REQUIRE(initial_packet != nullptr); - vn.negotiate(*initial_packet); + + auto blocks = initial_packet->header_block(); + blocks->next = initial_packet->payload_block(); + QUICInitialPacketR received_initial_packet(nullptr, {}, {}, blocks, 0); + vn.negotiate(received_initial_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); } @@ -59,11 +67,15 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_SUPPORTED_VERSIONS[0]); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - + packet_factory.create_initial_packet(packet_buf, {}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); REQUIRE(initial_packet != nullptr); - vn.negotiate(*initial_packet); + + auto blocks = initial_packet->header_block(); + blocks->next = initial_packet->payload_block(); + QUICInitialPacketR received_initial_packet(nullptr, {}, {}, blocks, 0); + vn.negotiate(received_initial_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); } @@ -74,11 +86,15 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_EXERCISE_VERSION); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - + packet_factory.create_initial_packet(packet_buf, {}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); REQUIRE(initial_packet != nullptr); - vn.negotiate(*initial_packet); + + auto blocks = initial_packet->header_block(); + blocks->next = initial_packet->payload_block(); + QUICInitialPacketR received_initial_packet(nullptr, {}, {}, blocks, 0); + vn.negotiate(received_initial_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NOT_NEGOTIATED); } } @@ -90,8 +106,10 @@ TEST_CASE("QUICVersionNegotiator - Client Side", "[quic]") QUICPacketFactory packet_factory(pp_key_info); QUICVersionNegotiator vn; - ats_unique_buf dummy_payload = ats_unique_malloc(128); - size_t dummy_payload_len = 128; + size_t dummy_payload_len = 128; + Ptr dummy_payload = make_ptr(new_IOBufferBlock()); + dummy_payload->alloc(iobuffer_size_to_index(dummy_payload_len, BUFFER_SIZE_INDEX_32K)); + dummy_payload->fill(dummy_payload_len); SECTION("Normal case") { @@ -108,17 +126,22 @@ TEST_CASE("QUICVersionNegotiator - Client Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_EXERCISE_VERSION); - QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - REQUIRE(initial_packet != nullptr); + 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); + REQUIRE(packet != nullptr); + 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()); + packet_factory.create_version_negotiation_packet(initial_packet.source_cid(), initial_packet.destination_cid()); REQUIRE(vn_packet != nullptr); + auto blocks = vn_packet->header_block(); + blocks->next = vn_packet->payload_block(); + QUICVersionNegotiationPacketR received_vn_packet(nullptr, {}, {}, blocks); // Negotiate version - vn.negotiate(*vn_packet); + vn.negotiate(received_vn_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); CHECK(vn.negotiated_version() == QUIC_SUPPORTED_VERSIONS[0]); } diff --git a/lib/records/RecHttp.cc b/lib/records/RecHttp.cc index 0d2165f43c2..351792db70e 100644 --- a/lib/records/RecHttp.cc +++ b/lib/records/RecHttp.cc @@ -395,8 +395,10 @@ HttpProxyPort::processOptions(const char *opts) af_set_p = true; } else if (0 == strcasecmp(OPT_SSL, item)) { m_type = TRANSPORT_SSL; +#if TS_USE_QUIC == 1 } else if (0 == strcasecmp(OPT_QUIC, item)) { m_type = TRANSPORT_QUIC; +#endif } else if (0 == strcasecmp(OPT_PLUGIN, item)) { m_type = TRANSPORT_PLUGIN; } else if (0 == strcasecmp(OPT_PROXY_PROTO, item)) { diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc index 557c3e67eff..24bc5c8ad09 100644 --- a/mgmt/RecordsConfig.cc +++ b/mgmt/RecordsConfig.cc @@ -1370,6 +1370,10 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.quic.client.cm_exercise_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} , + {RECT_CONFIG, "proxy.config.quic.client.quantum_readiness_test_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} + , + {RECT_CONFIG, "proxy.config.quic.server.quantum_readiness_test_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} + , {RECT_CONFIG, "proxy.config.quic.server.supported_groups", RECD_STRING, "P-256:X25519:P-384:P-521" , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} , {RECT_CONFIG, "proxy.config.quic.client.supported_groups", RECD_STRING, "P-256:X25519:P-384:P-521" , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} @@ -1378,6 +1382,8 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.quic.client.keylog_file", RECD_STRING, nullptr , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} , + {RECT_CONFIG, "proxy.config.quic.qlog_dir", RECD_STRING, nullptr , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} + , // Transport Parameters {RECT_CONFIG, "proxy.config.quic.no_activity_timeout_in", RECD_INT, "30000", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , @@ -1423,6 +1429,8 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.quic.active_cid_limit_out", RECD_INT, "8", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , + {RECT_CONFIG, "proxy.config.quic.disable_active_migration", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "[0-1]", RECA_NULL} + , // Constants of Loss Detection {RECT_CONFIG, "proxy.config.quic.loss_detection.packet_threshold", RECD_INT, "3", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , diff --git a/proxy/http/HttpProxyServerMain.cc b/proxy/http/HttpProxyServerMain.cc index 9c80490ef8d..98941b31169 100644 --- a/proxy/http/HttpProxyServerMain.cc +++ b/proxy/http/HttpProxyServerMain.cc @@ -40,6 +40,7 @@ #include "HttpConnectionCount.h" #include "HttpProxyServerMain.h" #if TS_USE_QUIC == 1 +#include "P_QUICNetProcessor.h" #include "P_QUICNextProtocolAccept.h" #include "http3/Http3SessionAccept.h" #endif @@ -237,12 +238,12 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor &acceptor, HttpProxyPort &port, unsigned quic->enableProtocols(port.m_session_protocol_preference); - // HTTP/3 - quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3, 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)); + // HTTP/3 + quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3, new Http3SessionAccept(accept_opt)); + quic->proxyPort = &port; acceptor._accept = quic; #endif diff --git a/proxy/http/Makefile.am b/proxy/http/Makefile.am index 31a06c1b086..edc10ff5a9b 100644 --- a/proxy/http/Makefile.am +++ b/proxy/http/Makefile.am @@ -33,7 +33,8 @@ AM_CPPFLAGS += \ -I$(abs_top_srcdir)/proxy/logging \ -I$(abs_top_srcdir)/proxy/http2 \ -I$(abs_top_srcdir)/proxy/http3 \ - $(TS_INCLUDES) + $(TS_INCLUDES) \ + @YAMLCPP_INCLUDES@ noinst_HEADERS = HttpProxyServerMain.h noinst_LIBRARIES = libhttp.a diff --git a/proxy/http3/Http09App.cc b/proxy/http3/Http09App.cc index 5635f7594c0..cbde81222ab 100644 --- a/proxy/http3/Http09App.cc +++ b/proxy/http3/Http09App.cc @@ -27,6 +27,7 @@ #include "P_Net.h" #include "P_VConnection.h" +#include "P_QUICNetVConnection.h" #include "QUICDebugNames.h" #include "Http3Session.h" diff --git a/proxy/http3/Http3App.cc b/proxy/http3/Http3App.cc index 38f7fccabda..dddcfea11fd 100644 --- a/proxy/http3/Http3App.cc +++ b/proxy/http3/Http3App.cc @@ -27,6 +27,7 @@ #include "P_Net.h" #include "P_VConnection.h" +#include "P_QUICNetVConnection.h" #include "Http3.h" #include "Http3Config.h" diff --git a/proxy/http3/Http3Frame.cc b/proxy/http3/Http3Frame.cc index b9102dc3afd..db8c98b1adf 100644 --- a/proxy/http3/Http3Frame.cc +++ b/proxy/http3/Http3Frame.cc @@ -241,11 +241,11 @@ Http3SettingsFrame::Http3SettingsFrame(const uint8_t *buf, size_t buf_len, uint3 } size_t id_len = QUICVariableInt::size(buf + len); - uint16_t id = QUICIntUtil::read_QUICVariableInt(buf + len); + uint16_t id = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len - len); len += id_len; size_t value_len = QUICVariableInt::size(buf + len); - uint64_t value = QUICIntUtil::read_QUICVariableInt(buf + len); + uint64_t value = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len - len); len += value_len; // Ignore any SETTINGS identifier it does not understand. diff --git a/proxy/http3/Http3HeaderFramer.cc b/proxy/http3/Http3HeaderFramer.cc index 43157dce5fc..4a86fd4e705 100644 --- a/proxy/http3/Http3HeaderFramer.cc +++ b/proxy/http3/Http3HeaderFramer.cc @@ -92,7 +92,7 @@ Http3HeaderFramer::_generate_header_block() http2_init_pseudo_headers(this->_header); parse_result = this->_header.parse_resp(&this->_http_parser, this->_source_vio->get_reader(), &bytes_used, false); } - this->_source_vio->ndone += this->_header.length_get(); + this->_source_vio->ndone += bytes_used; switch (parse_result) { case PARSE_RESULT_DONE: { diff --git a/proxy/http3/Http3Session.cc b/proxy/http3/Http3Session.cc index db56c3ca498..4d9ceaf066e 100644 --- a/proxy/http3/Http3Session.cc +++ b/proxy/http3/Http3Session.cc @@ -22,6 +22,7 @@ */ #include "Http3Session.h" +#include "P_QUICNetVConnection.h" #include "Http3.h" diff --git a/proxy/http3/Http3SessionAccept.cc b/proxy/http3/Http3SessionAccept.cc index 9763f95c067..f5d1ff66419 100644 --- a/proxy/http3/Http3SessionAccept.cc +++ b/proxy/http3/Http3SessionAccept.cc @@ -22,6 +22,7 @@ */ #include "Http3SessionAccept.h" +#include "P_QUICNetVConnection.h" #include "P_Net.h" #include "I_Machine.h" diff --git a/proxy/http3/Http3StreamDataVIOAdaptor.cc b/proxy/http3/Http3StreamDataVIOAdaptor.cc index e5b6875c209..15712e1c5c8 100644 --- a/proxy/http3/Http3StreamDataVIOAdaptor.cc +++ b/proxy/http3/Http3StreamDataVIOAdaptor.cc @@ -42,6 +42,13 @@ Http3StreamDataVIOAdaptor::handle_frame(std::shared_ptr frame) MIOBuffer *writer = this->_sink_vio->get_writer(); writer->write(dframe->payload(), dframe->payload_length()); + this->_total_data_length += dframe->payload_length(); return Http3ErrorUPtr(new Http3NoError()); } + +void +Http3StreamDataVIOAdaptor::finalize() +{ + this->_sink_vio->nbytes = this->_total_data_length; +} diff --git a/proxy/http3/Http3StreamDataVIOAdaptor.h b/proxy/http3/Http3StreamDataVIOAdaptor.h index f62fd319c64..19ac384320a 100644 --- a/proxy/http3/Http3StreamDataVIOAdaptor.h +++ b/proxy/http3/Http3StreamDataVIOAdaptor.h @@ -36,6 +36,10 @@ class Http3StreamDataVIOAdaptor : public Http3FrameHandler std::vector interests() override; Http3ErrorUPtr handle_frame(std::shared_ptr frame) override; + // Http3StreamDataVIOAdaptor + void finalize(); + private: - VIO *_sink_vio = nullptr; + VIO *_sink_vio = nullptr; + int64_t _total_data_length = 0; }; diff --git a/proxy/http3/Http3Transaction.cc b/proxy/http3/Http3Transaction.cc index 519ef76d39e..fc9d066cdd7 100644 --- a/proxy/http3/Http3Transaction.cc +++ b/proxy/http3/Http3Transaction.cc @@ -22,6 +22,7 @@ */ #include "Http3Transaction.h" +#include "P_QUICNetVConnection.h" #include "QUICDebugNames.h" @@ -156,8 +157,11 @@ HQTransaction::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, this->_write_vio.vc_server = this; this->_write_vio.op = VIO::WRITE; - this->_process_write_vio(); - this->_send_tracked_event(this->_write_event, VC_EVENT_WRITE_READY, &this->_write_vio); + if (c != nullptr && nbytes > 0) { + // TODO Return nullptr if the stream is not on writable state + this->_process_write_vio(); + this->_send_tracked_event(this->_write_event, VC_EVENT_WRITE_READY, &this->_write_vio); + } return &this->_write_vio; } @@ -280,7 +284,7 @@ HQTransaction::_signal_read_event() if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) { return; } - int event = this->_read_vio.ntodo() ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; + int event = this->_read_vio.nbytes == INT64_MAX ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread()); if (lock.is_locked()) { @@ -364,17 +368,20 @@ Http3Transaction::state_stream_open(int event, void *edata) switch (event) { case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); - int64_t len = this->_process_read_vio(); // if no progress, don't need to signal - if (len > 0) { + if (this->_process_read_vio() > 0) { this->_signal_read_event(); } this->_stream_io->read_reenable(); - break; - } + case VC_EVENT_READ_COMPLETE: + Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); + this->_process_read_vio(); + this->_data_handler->finalize(); + this->_signal_read_event(); + this->_stream_io->read_reenable(); + break; case VC_EVENT_WRITE_READY: case VC_EVENT_WRITE_COMPLETE: { Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); diff --git a/proxy/http3/Http3Transaction.h b/proxy/http3/Http3Transaction.h index 1bed5b058fd..5eebfbad310 100644 --- a/proxy/http3/Http3Transaction.h +++ b/proxy/http3/Http3Transaction.h @@ -34,6 +34,7 @@ class Http09Session; class Http3Session; class Http3HeaderFramer; class Http3DataFramer; +class Http3StreamDataVIOAdaptor; class HQTransaction : public ProxyTransaction { @@ -115,10 +116,10 @@ class Http3Transaction : public HQTransaction // These are for HTTP/3 Http3FrameDispatcher _frame_dispatcher; Http3FrameCollector _frame_collector; - Http3FrameGenerator *_header_framer = nullptr; - Http3FrameGenerator *_data_framer = nullptr; - Http3FrameHandler *_header_handler = nullptr; - Http3FrameHandler *_data_handler = nullptr; + Http3FrameGenerator *_header_framer = nullptr; + Http3FrameGenerator *_data_framer = nullptr; + Http3FrameHandler *_header_handler = nullptr; + Http3StreamDataVIOAdaptor *_data_handler = nullptr; }; /** diff --git a/proxy/http3/Makefile.am b/proxy/http3/Makefile.am index 7c626094029..979f3a656e9 100644 --- a/proxy/http3/Makefile.am +++ b/proxy/http3/Makefile.am @@ -31,7 +31,8 @@ AM_CPPFLAGS += \ -I$(abs_top_srcdir)/proxy/hdrs \ -I$(abs_top_srcdir)/proxy/shared \ -I$(abs_top_srcdir)/proxy/http/remap \ - $(TS_INCLUDES) + $(TS_INCLUDES) \ + @YAMLCPP_INCLUDES@ noinst_LIBRARIES = libhttp3.a diff --git a/proxy/http3/test/test_QPACK.cc b/proxy/http3/test/test_QPACK.cc index 179cd57848e..211030764e8 100644 --- a/proxy/http3/test/test_QPACK.cc +++ b/proxy/http3/test/test_QPACK.cc @@ -41,7 +41,7 @@ extern char appname[256]; extern char pattern[256]; constexpr int ACK_MODE_IMMEDIATE = 1; -constexpr int ACK_MODE_NONE = 0; +// constexpr int ACK_MODE_NONE = 0; constexpr int MAX_SEQUENCE = 1024; diff --git a/src/traffic_quic/Makefile.inc b/src/traffic_quic/Makefile.inc index a0fbf471f97..c00faf01e8a 100644 --- a/src/traffic_quic/Makefile.inc +++ b/src/traffic_quic/Makefile.inc @@ -33,7 +33,7 @@ traffic_quic_traffic_quic_CPPFLAGS = \ -I$(abs_top_srcdir)/proxy/logging \ -I$(abs_top_srcdir)/proxy/shared \ $(TS_INCLUDES) \ - @OPENSSL_INCLUDES@ + @OPENSSL_INCLUDES@ @YAMLCPP_INCLUDES@ traffic_quic_traffic_quic_LDFLAGS = \ $(AM_LDFLAGS) \ diff --git a/src/traffic_quic/quic_client.cc b/src/traffic_quic/quic_client.cc index c6d15b73ef0..8fc6e373a9b 100644 --- a/src/traffic_quic/quic_client.cc +++ b/src/traffic_quic/quic_client.cc @@ -28,13 +28,14 @@ #include #include "Http3Transaction.h" +#include "P_QUICNetVConnection.h" // OpenSSL protocol-lists format (vector of 8-bit length-prefixed, byte strings) // 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-23"sv); -static constexpr std::string_view H3_ALPN_PROTO_LIST("\5h3-23"sv); +static constexpr std::string_view HQ_ALPN_PROTO_LIST("\5hq-27"sv); +static constexpr std::string_view H3_ALPN_PROTO_LIST("\5h3-27"sv); QUICClient::QUICClient(const QUICClientConfig *config) : Continuation(new_ProxyMutex()), _config(config) { @@ -80,7 +81,11 @@ QUICClient::start(int, void *) opt.socket_recv_bufsize = 1048576; opt.socket_send_bufsize = 1048576; opt.alpn_protos = alpn_protos; - opt.set_sni_servername(this->_config->addr, strnlen(this->_config->addr, 1023)); + if (strlen(this->_config->server_name) == 0) { + opt.set_sni_servername(this->_config->addr, strnlen(this->_config->addr, 1023)); + } else { + opt.set_sni_servername(this->_config->server_name, strnlen(this->_config->server_name, 1023)); + } SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); @@ -224,7 +229,8 @@ Http09ClientApp::main_event_handler(int event, Event *data) if (stream_io->is_read_done() && this->_config->close) { // Connection Close Exercise - this->_qc->close(QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise"))); + this->_qc->close_quic_connection( + QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise"))); } break; @@ -272,7 +278,16 @@ Http3ClientApp::start() this->_resp_buf = new_MIOBuffer(BUFFER_SIZE_INDEX_32K); IOBufferReader *resp_buf_reader = _resp_buf->alloc_reader(); - this->_resp_handler = new RespHandler(this->_config, resp_buf_reader); + this->_resp_handler = new RespHandler(this->_config, resp_buf_reader, [&](void) { + if (this->_config->close) { + // Connection Close Exercise + this->_qc->close_quic_connection( + QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise"))); + } else if (this->_config->reset) { + // Stateless Reset Excercise + this->_qc->reset_quic_connection(); + } + }); super::start(); this->_do_http_request(); @@ -309,7 +324,13 @@ Http3ClientApp::_do_http_request() format = "GET https://%s/%s HTTP/1.1\r\n\r\n"; } - int request_len = snprintf(request, sizeof(request), format.c_str(), this->_config->addr, this->_config->path); + const char *authority; + if (strlen(this->_config->server_name) == 0) { + authority = this->_config->addr; + } else { + authority = this->_config->server_name; + } + int request_len = snprintf(request, sizeof(request), format.c_str(), authority, this->_config->path); Http09ClientAppDebug("\n%s", request); @@ -322,8 +343,8 @@ Http3ClientApp::_do_http_request() // // Response Handler // -RespHandler::RespHandler(const QUICClientConfig *config, IOBufferReader *reader) - : Continuation(new_ProxyMutex()), _config(config), _reader(reader) +RespHandler::RespHandler(const QUICClientConfig *config, IOBufferReader *reader, std::function on_complete) + : Continuation(new_ProxyMutex()), _config(config), _reader(reader), _on_complete(on_complete) { if (this->_config->output[0] != 0x0) { this->_filename = this->_config->output; @@ -372,6 +393,10 @@ RespHandler::main_event_handler(int event, Event *data) std::cout.rdbuf(default_stream); } + if (event == VC_EVENT_READ_COMPLETE) { + this->_on_complete(); + } + break; } case VC_EVENT_WRITE_READY: diff --git a/src/traffic_quic/quic_client.h b/src/traffic_quic/quic_client.h index 9af9f0d3593..fc12dbc436c 100644 --- a/src/traffic_quic/quic_client.h +++ b/src/traffic_quic/quic_client.h @@ -38,8 +38,10 @@ struct QUICClientConfig { char output[1024] = {0}; char port[16] = "4433"; char path[1018] = "/"; + char server_name[128] = ""; char debug_tags[1024] = "quic|vv_quic_crypto|http3|qpack"; int close = false; + int reset = false; int http0_9 = true; int http3 = false; }; @@ -47,7 +49,7 @@ struct QUICClientConfig { class RespHandler : public Continuation { public: - RespHandler(const QUICClientConfig *config, IOBufferReader *reader); + RespHandler(const QUICClientConfig *config, IOBufferReader *reader, std::function on_complete); int main_event_handler(int event, Event *data); void set_read_vio(VIO *vio); @@ -56,6 +58,7 @@ class RespHandler : public Continuation const char *_filename = nullptr; IOBufferReader *_reader = nullptr; VIO *_read_vio = nullptr; + std::function _on_complete; }; class QUICClient : public Continuation diff --git a/src/traffic_quic/traffic_quic.cc b/src/traffic_quic/traffic_quic.cc index 69e23506ee0..00ac53a303a 100644 --- a/src/traffic_quic/traffic_quic.cc +++ b/src/traffic_quic/traffic_quic.cc @@ -61,8 +61,10 @@ main(int argc, const char **argv) {"output", 'o', "Write to FILE instead of stdout", "S1023", config.output, nullptr, nullptr}, {"port", 'p', "Port", "S15", config.port, nullptr, nullptr}, {"path", 'P', "Path", "S1017", config.path, nullptr, nullptr}, + {"server", 's', "Server name", "S127", config.server_name, nullptr, nullptr}, {"debug", 'T', "Vertical-bar-separated Debug Tags", "S1023", config.debug_tags, nullptr, nullptr}, {"close", 'c', "Enable connection close excercise", "F", &config.close, nullptr, nullptr}, + {"reset", 'r', "Enable stateless reset excercise", "F", &config.reset, nullptr, nullptr}, {"http0_9", '-', "Enable HTTP/0.9", "T", &config.http0_9, nullptr, nullptr}, {"http3", '-', "Enable HTTP/3", "F", &config.http3, nullptr, nullptr}, diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc index ac1d953198c..84df79c1ca6 100644 --- a/src/traffic_server/traffic_server.cc +++ b/src/traffic_server/traffic_server.cc @@ -67,6 +67,7 @@ extern "C" int plock(int); #include "tscore/signals.h" #include "P_EventSystem.h" #include "P_Net.h" +#include "P_QUICNetProcessor.h" #include "P_UDPNet.h" #include "P_DNS.h" #include "P_SplitDNS.h" diff --git a/src/tscore/ink_inet.cc b/src/tscore/ink_inet.cc index 9f36ce9f9a8..5ba9bc3c614 100644 --- a/src/tscore/ink_inet.cc +++ b/src/tscore/ink_inet.cc @@ -50,8 +50,8 @@ 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-23"sv); // HTTP/0.9 over QUIC -const std::string_view IP_PROTO_TAG_HTTP_3("h3-23"sv); // HTTP/3 over QUIC +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 UNIX_PROTO_TAG{"unix"sv};