// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "quic/core/quic_utils.h" #include #include #include #include #include #include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_constants.h" #include "quic/core/quic_types.h" #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "common/platform/api/quiche_logging.h" #include "common/platform/api/quiche_prefetch.h" #include "common/quiche_endian.h" namespace quic { namespace { // We know that >= GCC 4.8 and Clang have a __uint128_t intrinsic. Other // compilers don't necessarily, notably MSVC. #if defined(__x86_64__) && \ ((defined(__GNUC__) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ defined(__clang__)) #define QUIC_UTIL_HAS_UINT128 1 #endif #ifdef QUIC_UTIL_HAS_UINT128 absl::uint128 IncrementalHashFast(absl::uint128 uhash, absl::string_view data) { // This code ends up faster than the naive implementation for 2 reasons: // 1. absl::uint128 is sufficiently complicated that the compiler // cannot transform the multiplication by kPrime into a shift-multiply-add; // it has go through all of the instructions for a 128-bit multiply. // 2. Because there are so fewer instructions (around 13), the hot loop fits // nicely in the instruction queue of many Intel CPUs. // kPrime = 309485009821345068724781371 static const absl::uint128 kPrime = (static_cast(16777216) << 64) + 315; auto hi = absl::Uint128High64(uhash); auto lo = absl::Uint128Low64(uhash); absl::uint128 xhash = (static_cast(hi) << 64) + lo; const uint8_t* octets = reinterpret_cast(data.data()); for (size_t i = 0; i < data.length(); ++i) { xhash = (xhash ^ static_cast(octets[i])) * kPrime; } return absl::MakeUint128(absl::Uint128High64(xhash), absl::Uint128Low64(xhash)); } #endif #ifndef QUIC_UTIL_HAS_UINT128 // Slow implementation of IncrementalHash. In practice, only used by Chromium. absl::uint128 IncrementalHashSlow(absl::uint128 hash, absl::string_view data) { // kPrime = 309485009821345068724781371 static const absl::uint128 kPrime = absl::MakeUint128(16777216, 315); const uint8_t* octets = reinterpret_cast(data.data()); for (size_t i = 0; i < data.length(); ++i) { hash = hash ^ absl::MakeUint128(0, octets[i]); hash = hash * kPrime; } return hash; } #endif absl::uint128 IncrementalHash(absl::uint128 hash, absl::string_view data) { #ifdef QUIC_UTIL_HAS_UINT128 return IncrementalHashFast(hash, data); #else return IncrementalHashSlow(hash, data); #endif } } // namespace // static uint64_t QuicUtils::FNV1a_64_Hash(absl::string_view data) { static const uint64_t kOffset = UINT64_C(14695981039346656037); static const uint64_t kPrime = UINT64_C(1099511628211); const uint8_t* octets = reinterpret_cast(data.data()); uint64_t hash = kOffset; for (size_t i = 0; i < data.length(); ++i) { hash = hash ^ octets[i]; hash = hash * kPrime; } return hash; } // static absl::uint128 QuicUtils::FNV1a_128_Hash(absl::string_view data) { return FNV1a_128_Hash_Three(data, absl::string_view(), absl::string_view()); } // static absl::uint128 QuicUtils::FNV1a_128_Hash_Two(absl::string_view data1, absl::string_view data2) { return FNV1a_128_Hash_Three(data1, data2, absl::string_view()); } // static absl::uint128 QuicUtils::FNV1a_128_Hash_Three(absl::string_view data1, absl::string_view data2, absl::string_view data3) { // The two constants are defined as part of the hash algorithm. // see http://www.isthe.com/chongo/tech/comp/fnv/ // kOffset = 144066263297769815596495629667062367629 const absl::uint128 kOffset = absl::MakeUint128( UINT64_C(7809847782465536322), UINT64_C(7113472399480571277)); absl::uint128 hash = IncrementalHash(kOffset, data1); if (data2.empty()) { return hash; } hash = IncrementalHash(hash, data2); if (data3.empty()) { return hash; } return IncrementalHash(hash, data3); } // static void QuicUtils::SerializeUint128Short(absl::uint128 v, uint8_t* out) { const uint64_t lo = absl::Uint128Low64(v); const uint64_t hi = absl::Uint128High64(v); // This assumes that the system is little-endian. memcpy(out, &lo, sizeof(lo)); memcpy(out + sizeof(lo), &hi, sizeof(hi) / 2); } #define RETURN_STRING_LITERAL(x) \ case x: \ return #x; std::string QuicUtils::AddressChangeTypeToString(AddressChangeType type) { switch (type) { RETURN_STRING_LITERAL(NO_CHANGE); RETURN_STRING_LITERAL(PORT_CHANGE); RETURN_STRING_LITERAL(IPV4_SUBNET_CHANGE); RETURN_STRING_LITERAL(IPV4_TO_IPV6_CHANGE); RETURN_STRING_LITERAL(IPV6_TO_IPV4_CHANGE); RETURN_STRING_LITERAL(IPV6_TO_IPV6_CHANGE); RETURN_STRING_LITERAL(IPV4_TO_IPV4_CHANGE); } return "INVALID_ADDRESS_CHANGE_TYPE"; } const char* QuicUtils::SentPacketStateToString(SentPacketState state) { switch (state) { RETURN_STRING_LITERAL(OUTSTANDING); RETURN_STRING_LITERAL(NEVER_SENT); RETURN_STRING_LITERAL(ACKED); RETURN_STRING_LITERAL(UNACKABLE); RETURN_STRING_LITERAL(NEUTERED); RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMITTED); RETURN_STRING_LITERAL(LOST); RETURN_STRING_LITERAL(TLP_RETRANSMITTED); RETURN_STRING_LITERAL(RTO_RETRANSMITTED); RETURN_STRING_LITERAL(PTO_RETRANSMITTED); RETURN_STRING_LITERAL(PROBE_RETRANSMITTED); RETURN_STRING_LITERAL(NOT_CONTRIBUTING_RTT); } return "INVALID_SENT_PACKET_STATE"; } // static const char* QuicUtils::QuicLongHeaderTypetoString(QuicLongHeaderType type) { switch (type) { RETURN_STRING_LITERAL(VERSION_NEGOTIATION); RETURN_STRING_LITERAL(INITIAL); RETURN_STRING_LITERAL(RETRY); RETURN_STRING_LITERAL(HANDSHAKE); RETURN_STRING_LITERAL(ZERO_RTT_PROTECTED); default: return "INVALID_PACKET_TYPE"; } } // static const char* QuicUtils::AckResultToString(AckResult result) { switch (result) { RETURN_STRING_LITERAL(PACKETS_NEWLY_ACKED); RETURN_STRING_LITERAL(NO_PACKETS_NEWLY_ACKED); RETURN_STRING_LITERAL(UNSENT_PACKETS_ACKED); RETURN_STRING_LITERAL(UNACKABLE_PACKETS_ACKED); RETURN_STRING_LITERAL(PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE); } return "INVALID_ACK_RESULT"; } // static AddressChangeType QuicUtils::DetermineAddressChangeType( const QuicSocketAddress& old_address, const QuicSocketAddress& new_address) { if (!old_address.IsInitialized() || !new_address.IsInitialized() || old_address == new_address) { return NO_CHANGE; } if (old_address.host() == new_address.host()) { return PORT_CHANGE; } bool old_ip_is_ipv4 = old_address.host().IsIPv4() ? true : false; bool migrating_ip_is_ipv4 = new_address.host().IsIPv4() ? true : false; if (old_ip_is_ipv4 && !migrating_ip_is_ipv4) { return IPV4_TO_IPV6_CHANGE; } if (!old_ip_is_ipv4) { return migrating_ip_is_ipv4 ? IPV6_TO_IPV4_CHANGE : IPV6_TO_IPV6_CHANGE; } const int kSubnetMaskLength = 24; if (old_address.host().InSameSubnet(new_address.host(), kSubnetMaskLength)) { // Subnet part does not change (here, we use /24), which is considered to be // caused by NATs. return IPV4_SUBNET_CHANGE; } return IPV4_TO_IPV4_CHANGE; } // static void QuicUtils::CopyToBuffer(const struct iovec* iov, int iov_count, size_t iov_offset, size_t buffer_length, char* buffer) { int iovnum = 0; while (iovnum < iov_count && iov_offset >= iov[iovnum].iov_len) { iov_offset -= iov[iovnum].iov_len; ++iovnum; } QUICHE_DCHECK_LE(iovnum, iov_count); QUICHE_DCHECK_LE(iov_offset, iov[iovnum].iov_len); if (iovnum >= iov_count || buffer_length == 0) { return; } // Unroll the first iteration that handles iov_offset. const size_t iov_available = iov[iovnum].iov_len - iov_offset; size_t copy_len = std::min(buffer_length, iov_available); // Try to prefetch the next iov if there is at least one more after the // current. Otherwise, it looks like an irregular access that the hardware // prefetcher won't speculatively prefetch. Only prefetch one iov because // generally, the iov_offset is not 0, input iov consists of 2K buffers and // the output buffer is ~1.4K. if (copy_len == iov_available && iovnum + 1 < iov_count) { char* next_base = static_cast(iov[iovnum + 1].iov_base); // Prefetch 2 cachelines worth of data to get the prefetcher started; leave // it to the hardware prefetcher after that. quiche::QuichePrefetchT0(next_base); if (iov[iovnum + 1].iov_len >= 64) { quiche::QuichePrefetchT0(next_base + ABSL_CACHELINE_SIZE); } } const char* src = static_cast(iov[iovnum].iov_base) + iov_offset; while (true) { memcpy(buffer, src, copy_len); buffer_length -= copy_len; buffer += copy_len; if (buffer_length == 0 || ++iovnum >= iov_count) { break; } src = static_cast(iov[iovnum].iov_base); copy_len = std::min(buffer_length, iov[iovnum].iov_len); } QUIC_BUG_IF(quic_bug_10839_1, buffer_length > 0) << "Failed to copy entire length to buffer."; } // static struct iovec QuicUtils::MakeIovec(absl::string_view data) { struct iovec iov = {const_cast(data.data()), static_cast(data.size())}; return iov; } // static bool QuicUtils::IsAckable(SentPacketState state) { return state != NEVER_SENT && state != ACKED && state != UNACKABLE; } // static bool QuicUtils::IsRetransmittableFrame(QuicFrameType type) { switch (type) { case ACK_FRAME: case PADDING_FRAME: case STOP_WAITING_FRAME: case MTU_DISCOVERY_FRAME: case PATH_CHALLENGE_FRAME: case PATH_RESPONSE_FRAME: return false; default: return true; } } // static bool QuicUtils::IsHandshakeFrame(const QuicFrame& frame, QuicTransportVersion transport_version) { if (!QuicVersionUsesCryptoFrames(transport_version)) { return frame.type == STREAM_FRAME && frame.stream_frame.stream_id == GetCryptoStreamId(transport_version); } else { return frame.type == CRYPTO_FRAME; } } // static bool QuicUtils::ContainsFrameType(const QuicFrames& frames, QuicFrameType type) { for (const QuicFrame& frame : frames) { if (frame.type == type) { return true; } } return false; } // static SentPacketState QuicUtils::RetransmissionTypeToPacketState( TransmissionType retransmission_type) { switch (retransmission_type) { case ALL_ZERO_RTT_RETRANSMISSION: return UNACKABLE; case HANDSHAKE_RETRANSMISSION: return HANDSHAKE_RETRANSMITTED; case LOSS_RETRANSMISSION: return LOST; case TLP_RETRANSMISSION: return TLP_RETRANSMITTED; case RTO_RETRANSMISSION: return RTO_RETRANSMITTED; case PTO_RETRANSMISSION: return PTO_RETRANSMITTED; case PROBING_RETRANSMISSION: return PROBE_RETRANSMITTED; case PATH_RETRANSMISSION: return NOT_CONTRIBUTING_RTT; case ALL_INITIAL_RETRANSMISSION: return UNACKABLE; default: QUIC_BUG(quic_bug_10839_2) << retransmission_type << " is not a retransmission_type"; return UNACKABLE; } } // static bool QuicUtils::IsIetfPacketHeader(uint8_t first_byte) { return (first_byte & FLAGS_LONG_HEADER) || (first_byte & FLAGS_FIXED_BIT) || !(first_byte & FLAGS_DEMULTIPLEXING_BIT); } // static bool QuicUtils::IsIetfPacketShortHeader(uint8_t first_byte) { return IsIetfPacketHeader(first_byte) && !(first_byte & FLAGS_LONG_HEADER); } // static QuicStreamId QuicUtils::GetInvalidStreamId(QuicTransportVersion version) { return VersionHasIetfQuicFrames(version) ? std::numeric_limits::max() : 0; } // static QuicStreamId QuicUtils::GetCryptoStreamId(QuicTransportVersion version) { QUIC_BUG_IF(quic_bug_12982_1, QuicVersionUsesCryptoFrames(version)) << "CRYPTO data aren't in stream frames; they have no stream ID."; return QuicVersionUsesCryptoFrames(version) ? GetInvalidStreamId(version) : 1; } // static bool QuicUtils::IsCryptoStreamId(QuicTransportVersion version, QuicStreamId stream_id) { if (QuicVersionUsesCryptoFrames(version)) { return false; } return stream_id == GetCryptoStreamId(version); } // static QuicStreamId QuicUtils::GetHeadersStreamId(QuicTransportVersion version) { QUICHE_DCHECK(!VersionUsesHttp3(version)); return GetFirstBidirectionalStreamId(version, Perspective::IS_CLIENT); } // static bool QuicUtils::IsClientInitiatedStreamId(QuicTransportVersion version, QuicStreamId id) { if (id == GetInvalidStreamId(version)) { return false; } return VersionHasIetfQuicFrames(version) ? id % 2 == 0 : id % 2 != 0; } // static bool QuicUtils::IsServerInitiatedStreamId(QuicTransportVersion version, QuicStreamId id) { if (id == GetInvalidStreamId(version)) { return false; } return VersionHasIetfQuicFrames(version) ? id % 2 != 0 : id % 2 == 0; } // static bool QuicUtils::IsOutgoingStreamId(ParsedQuicVersion version, QuicStreamId id, Perspective perspective) { // Streams are outgoing streams, iff: // - we are the server and the stream is server-initiated // - we are the client and the stream is client-initiated. const bool perspective_is_server = perspective == Perspective::IS_SERVER; const bool stream_is_server = QuicUtils::IsServerInitiatedStreamId(version.transport_version, id); return perspective_is_server == stream_is_server; } // static bool QuicUtils::IsBidirectionalStreamId(QuicStreamId id, ParsedQuicVersion version) { QUICHE_DCHECK(version.HasIetfQuicFrames()); return id % 4 < 2; } // static StreamType QuicUtils::GetStreamType(QuicStreamId id, Perspective perspective, bool peer_initiated, ParsedQuicVersion version) { QUICHE_DCHECK(version.HasIetfQuicFrames()); if (IsBidirectionalStreamId(id, version)) { return BIDIRECTIONAL; } if (peer_initiated) { if (perspective == Perspective::IS_SERVER) { QUICHE_DCHECK_EQ(2u, id % 4); } else { QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective); QUICHE_DCHECK_EQ(3u, id % 4); } return READ_UNIDIRECTIONAL; } if (perspective == Perspective::IS_SERVER) { QUICHE_DCHECK_EQ(3u, id % 4); } else { QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective); QUICHE_DCHECK_EQ(2u, id % 4); } return WRITE_UNIDIRECTIONAL; } // static QuicStreamId QuicUtils::StreamIdDelta(QuicTransportVersion version) { return VersionHasIetfQuicFrames(version) ? 4 : 2; } // static QuicStreamId QuicUtils::GetFirstBidirectionalStreamId( QuicTransportVersion version, Perspective perspective) { if (VersionHasIetfQuicFrames(version)) { return perspective == Perspective::IS_CLIENT ? 0 : 1; } else if (QuicVersionUsesCryptoFrames(version)) { return perspective == Perspective::IS_CLIENT ? 1 : 2; } return perspective == Perspective::IS_CLIENT ? 3 : 2; } // static QuicStreamId QuicUtils::GetFirstUnidirectionalStreamId( QuicTransportVersion version, Perspective perspective) { if (VersionHasIetfQuicFrames(version)) { return perspective == Perspective::IS_CLIENT ? 2 : 3; } else if (QuicVersionUsesCryptoFrames(version)) { return perspective == Perspective::IS_CLIENT ? 1 : 2; } return perspective == Perspective::IS_CLIENT ? 3 : 2; } // static QuicStreamId QuicUtils::GetMaxClientInitiatedBidirectionalStreamId( QuicTransportVersion version) { if (VersionHasIetfQuicFrames(version)) { // Client initiated bidirectional streams have stream IDs divisible by 4. return std::numeric_limits::max() - 3; } // Client initiated bidirectional streams have odd stream IDs. return std::numeric_limits::max(); } // static QuicConnectionId QuicUtils::CreateReplacementConnectionId( const QuicConnectionId& connection_id) { return CreateReplacementConnectionId(connection_id, kQuicDefaultConnectionIdLength); } // static QuicConnectionId QuicUtils::CreateReplacementConnectionId( const QuicConnectionId& connection_id, uint8_t expected_connection_id_length) { if (expected_connection_id_length == 0) { return EmptyQuicConnectionId(); } const uint64_t connection_id_hash64 = FNV1a_64_Hash( absl::string_view(connection_id.data(), connection_id.length())); if (expected_connection_id_length <= sizeof(uint64_t)) { return QuicConnectionId( reinterpret_cast(&connection_id_hash64), expected_connection_id_length); } char new_connection_id_data[255] = {}; const absl::uint128 connection_id_hash128 = FNV1a_128_Hash( absl::string_view(connection_id.data(), connection_id.length())); static_assert(sizeof(connection_id_hash64) + sizeof(connection_id_hash128) <= sizeof(new_connection_id_data), "bad size"); memcpy(new_connection_id_data, &connection_id_hash64, sizeof(connection_id_hash64)); memcpy(new_connection_id_data + sizeof(connection_id_hash64), &connection_id_hash128, sizeof(connection_id_hash128)); return QuicConnectionId(new_connection_id_data, expected_connection_id_length); } // static QuicConnectionId QuicUtils::CreateRandomConnectionId() { return CreateRandomConnectionId(kQuicDefaultConnectionIdLength, QuicRandom::GetInstance()); } // static QuicConnectionId QuicUtils::CreateRandomConnectionId(QuicRandom* random) { return CreateRandomConnectionId(kQuicDefaultConnectionIdLength, random); } // static QuicConnectionId QuicUtils::CreateRandomConnectionId( uint8_t connection_id_length) { return CreateRandomConnectionId(connection_id_length, QuicRandom::GetInstance()); } // static QuicConnectionId QuicUtils::CreateRandomConnectionId( uint8_t connection_id_length, QuicRandom* random) { QuicConnectionId connection_id; connection_id.set_length(connection_id_length); if (connection_id.length() > 0) { random->RandBytes(connection_id.mutable_data(), connection_id.length()); } return connection_id; } // static QuicConnectionId QuicUtils::CreateZeroConnectionId( QuicTransportVersion version) { if (!VersionAllowsVariableLengthConnectionIds(version)) { char connection_id_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; return QuicConnectionId(static_cast(connection_id_bytes), ABSL_ARRAYSIZE(connection_id_bytes)); } return EmptyQuicConnectionId(); } // static bool QuicUtils::IsConnectionIdLengthValidForVersion( size_t connection_id_length, QuicTransportVersion transport_version) { // No version of QUIC can support lengths that do not fit in an uint8_t. if (connection_id_length > static_cast(std::numeric_limits::max())) { return false; } if (transport_version == QUIC_VERSION_UNSUPPORTED || transport_version == QUIC_VERSION_RESERVED_FOR_NEGOTIATION) { // Unknown versions could allow connection ID lengths up to 255. return true; } const uint8_t connection_id_length8 = static_cast(connection_id_length); // Versions that do not support variable lengths only support length 8. if (!VersionAllowsVariableLengthConnectionIds(transport_version)) { return connection_id_length8 == kQuicDefaultConnectionIdLength; } // Versions that do support variable length but do not have length-prefixed // connection IDs use the 4-bit connection ID length encoding which can // only encode values 0 and 4-18. if (!VersionHasLengthPrefixedConnectionIds(transport_version)) { return connection_id_length8 == 0 || (connection_id_length8 >= 4 && connection_id_length8 <= kQuicMaxConnectionId4BitLength); } return connection_id_length8 <= kQuicMaxConnectionIdWithLengthPrefixLength; } // static bool QuicUtils::IsConnectionIdValidForVersion( QuicConnectionId connection_id, QuicTransportVersion transport_version) { return IsConnectionIdLengthValidForVersion(connection_id.length(), transport_version); } StatelessResetToken QuicUtils::GenerateStatelessResetToken( QuicConnectionId connection_id) { static_assert(sizeof(absl::uint128) == sizeof(StatelessResetToken), "bad size"); static_assert(alignof(absl::uint128) >= alignof(StatelessResetToken), "bad alignment"); absl::uint128 hash = FNV1a_128_Hash( absl::string_view(connection_id.data(), connection_id.length())); return *reinterpret_cast(&hash); } // static QuicStreamCount QuicUtils::GetMaxStreamCount() { return (kMaxQuicStreamCount >> 2) + 1; } // static PacketNumberSpace QuicUtils::GetPacketNumberSpace( EncryptionLevel encryption_level) { switch (encryption_level) { case ENCRYPTION_INITIAL: return INITIAL_DATA; case ENCRYPTION_HANDSHAKE: return HANDSHAKE_DATA; case ENCRYPTION_ZERO_RTT: case ENCRYPTION_FORWARD_SECURE: return APPLICATION_DATA; default: QUIC_BUG(quic_bug_10839_3) << "Try to get packet number space of encryption level: " << encryption_level; return NUM_PACKET_NUMBER_SPACES; } } // static EncryptionLevel QuicUtils::GetEncryptionLevel( PacketNumberSpace packet_number_space) { switch (packet_number_space) { case INITIAL_DATA: return ENCRYPTION_INITIAL; case HANDSHAKE_DATA: return ENCRYPTION_HANDSHAKE; case APPLICATION_DATA: return ENCRYPTION_FORWARD_SECURE; default: QUICHE_DCHECK(false); return NUM_ENCRYPTION_LEVELS; } } // static bool QuicUtils::IsProbingFrame(QuicFrameType type) { switch (type) { case PATH_CHALLENGE_FRAME: case PATH_RESPONSE_FRAME: case NEW_CONNECTION_ID_FRAME: case PADDING_FRAME: return true; default: return false; } } bool IsValidWebTransportSessionId(WebTransportSessionId id, ParsedQuicVersion version) { QUICHE_DCHECK(version.UsesHttp3()); return (id <= std::numeric_limits::max()) && QuicUtils::IsBidirectionalStreamId(id, version) && QuicUtils::IsClientInitiatedStreamId(version.transport_version, id); } #undef RETURN_STRING_LITERAL // undef for jumbo builds } // namespace quic