LCOV - code coverage report
Current view: top level - source/common/quic - envoy_quic_utils.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 42 221 19.0 %
Date: 2024-01-05 06:35:25 Functions: 5 16 31.2 %

          Line data    Source code
       1             : #include "source/common/quic/envoy_quic_utils.h"
       2             : 
       3             : #include <memory>
       4             : 
       5             : #include "envoy/common/platform.h"
       6             : #include "envoy/config/core/v3/base.pb.h"
       7             : 
       8             : #include "source/common/http/utility.h"
       9             : #include "source/common/network/socket_option_factory.h"
      10             : #include "source/common/network/utility.h"
      11             : 
      12             : namespace Envoy {
      13             : namespace Quic {
      14             : 
      15             : // TODO(danzh): this is called on each write. Consider to return an address instance on the stack if
      16             : // the heap allocation is too expensive.
      17             : Network::Address::InstanceConstSharedPtr
      18           0 : quicAddressToEnvoyAddressInstance(const quic::QuicSocketAddress& quic_address) {
      19           0 :   return quic_address.IsInitialized()
      20           0 :              ? Network::Address::addressFromSockAddrOrDie(quic_address.generic_address(),
      21           0 :                                                           quic_address.host().address_family() ==
      22           0 :                                                                   quiche::IpAddressFamily::IP_V4
      23           0 :                                                               ? sizeof(sockaddr_in)
      24           0 :                                                               : sizeof(sockaddr_in6),
      25           0 :                                                           -1, false)
      26           0 :              : nullptr;
      27           0 : }
      28             : 
      29           0 : quic::QuicSocketAddress envoyIpAddressToQuicSocketAddress(const Network::Address::Ip* envoy_ip) {
      30           0 :   if (envoy_ip == nullptr) {
      31             :     // Return uninitialized socket addr
      32           0 :     return {};
      33           0 :   }
      34             : 
      35           0 :   uint32_t port = envoy_ip->port();
      36           0 :   sockaddr_storage ss;
      37             : 
      38           0 :   if (envoy_ip->version() == Network::Address::IpVersion::v4) {
      39             :     // Create and return quic ipv4 address
      40           0 :     auto ipv4_addr = reinterpret_cast<sockaddr_in*>(&ss);
      41           0 :     memset(ipv4_addr, 0, sizeof(sockaddr_in));
      42           0 :     ipv4_addr->sin_family = AF_INET;
      43           0 :     ipv4_addr->sin_port = htons(port);
      44           0 :     ipv4_addr->sin_addr.s_addr = envoy_ip->ipv4()->address();
      45           0 :   } else {
      46             :     // Create and return quic ipv6 address
      47           0 :     auto ipv6_addr = reinterpret_cast<sockaddr_in6*>(&ss);
      48           0 :     memset(ipv6_addr, 0, sizeof(sockaddr_in6));
      49           0 :     ipv6_addr->sin6_family = AF_INET6;
      50           0 :     ipv6_addr->sin6_port = htons(port);
      51           0 :     ASSERT(sizeof(ipv6_addr->sin6_addr.s6_addr) == 16u);
      52           0 :     *reinterpret_cast<absl::uint128*>(ipv6_addr->sin6_addr.s6_addr) = envoy_ip->ipv6()->address();
      53           0 :   }
      54           0 :   return quic::QuicSocketAddress(ss);
      55           0 : }
      56             : 
      57           0 : spdy::Http2HeaderBlock envoyHeadersToHttp2HeaderBlock(const Http::HeaderMap& headers) {
      58           0 :   spdy::Http2HeaderBlock header_block;
      59           0 :   headers.iterate([&header_block](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate {
      60             :     // The key-value pairs are copied.
      61           0 :     header_block.AppendValueOrAddHeader(header.key().getStringView(),
      62           0 :                                         header.value().getStringView());
      63           0 :     return Http::HeaderMap::Iterate::Continue;
      64           0 :   });
      65           0 :   return header_block;
      66           0 : }
      67             : 
      68           0 : quic::QuicRstStreamErrorCode envoyResetReasonToQuicRstError(Http::StreamResetReason reason) {
      69           0 :   switch (reason) {
      70           0 :   case Http::StreamResetReason::LocalRefusedStreamReset:
      71           0 :     return quic::QUIC_REFUSED_STREAM;
      72           0 :   case Http::StreamResetReason::LocalConnectionFailure:
      73           0 :   case Http::StreamResetReason::RemoteConnectionFailure:
      74           0 :   case Http::StreamResetReason::ConnectionTimeout:
      75           0 :   case Http::StreamResetReason::ConnectionTermination:
      76           0 :     return quic::QUIC_STREAM_CONNECTION_ERROR;
      77           0 :   case Http::StreamResetReason::LocalReset:
      78           0 :   case Http::StreamResetReason::OverloadManager:
      79           0 :     return quic::QUIC_STREAM_CANCELLED;
      80           0 :   default:
      81           0 :     return quic::QUIC_BAD_APPLICATION_PAYLOAD;
      82           0 :   }
      83           0 : }
      84             : 
      85           2 : Http::StreamResetReason quicRstErrorToEnvoyLocalResetReason(quic::QuicRstStreamErrorCode rst_err) {
      86           2 :   switch (rst_err) {
      87           0 :   case quic::QUIC_REFUSED_STREAM:
      88           0 :     return Http::StreamResetReason::LocalRefusedStreamReset;
      89           0 :   case quic::QUIC_STREAM_CONNECTION_ERROR:
      90           0 :     return Http::StreamResetReason::LocalConnectionFailure;
      91           2 :   case quic::QUIC_BAD_APPLICATION_PAYLOAD:
      92           2 :     return Http::StreamResetReason::ProtocolError;
      93           0 :   default:
      94           0 :     return Http::StreamResetReason::LocalReset;
      95           2 :   }
      96           2 : }
      97             : 
      98           8 : Http::StreamResetReason quicRstErrorToEnvoyRemoteResetReason(quic::QuicRstStreamErrorCode rst_err) {
      99           8 :   switch (rst_err) {
     100           0 :   case quic::QUIC_REFUSED_STREAM:
     101           0 :     return Http::StreamResetReason::RemoteRefusedStreamReset;
     102           0 :   case quic::QUIC_STREAM_CONNECTION_ERROR:
     103           0 :     return Http::StreamResetReason::ConnectError;
     104           8 :   default:
     105           8 :     return Http::StreamResetReason::RemoteReset;
     106           8 :   }
     107           8 : }
     108             : 
     109             : Http::StreamResetReason quicErrorCodeToEnvoyLocalResetReason(quic::QuicErrorCode error,
     110         229 :                                                              bool connected) {
     111         229 :   switch (error) {
     112           0 :   case quic::QUIC_HANDSHAKE_FAILED:
     113           0 :   case quic::QUIC_HANDSHAKE_TIMEOUT:
     114           0 :     return Http::StreamResetReason::LocalConnectionFailure;
     115           0 :   case quic::QUIC_PACKET_WRITE_ERROR:
     116           0 :   case quic::QUIC_NETWORK_IDLE_TIMEOUT:
     117           0 :     return connected ? Http::StreamResetReason::ConnectionTermination
     118           0 :                      : Http::StreamResetReason::LocalConnectionFailure;
     119         171 :   case quic::QUIC_HTTP_FRAME_ERROR:
     120         171 :     return Http::StreamResetReason::ProtocolError;
     121          58 :   default:
     122          58 :     return Http::StreamResetReason::ConnectionTermination;
     123         229 :   }
     124         229 : }
     125             : 
     126           1 : Http::StreamResetReason quicErrorCodeToEnvoyRemoteResetReason(quic::QuicErrorCode error) {
     127           1 :   switch (error) {
     128           0 :   case quic::QUIC_HANDSHAKE_FAILED:
     129           0 :   case quic::QUIC_HANDSHAKE_TIMEOUT:
     130           0 :     return Http::StreamResetReason::RemoteConnectionFailure;
     131           1 :   default:
     132           1 :     return Http::StreamResetReason::ConnectionTermination;
     133           1 :   }
     134           1 : }
     135             : 
     136             : Network::ConnectionSocketPtr
     137             : createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr,
     138             :                        Network::Address::InstanceConstSharedPtr& local_addr,
     139         515 :                        const Network::ConnectionSocket::OptionsSharedPtr& options) {
     140         515 :   if (local_addr == nullptr) {
     141           0 :     local_addr = Network::Utility::getLocalAddress(peer_addr->ip()->version());
     142           0 :   }
     143         515 :   auto connection_socket = std::make_unique<Network::ConnectionSocketImpl>(
     144         515 :       Network::Socket::Type::Datagram, local_addr, peer_addr, Network::SocketCreationOptions{});
     145         515 :   connection_socket->addOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions());
     146         515 :   connection_socket->addOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions());
     147         515 :   if (options != nullptr) {
     148           0 :     connection_socket->addOptions(options);
     149           0 :   }
     150         515 :   if (!Network::Socket::applyOptions(connection_socket->options(), *connection_socket,
     151         515 :                                      envoy::config::core::v3::SocketOption::STATE_PREBIND)) {
     152           0 :     connection_socket->close();
     153           0 :     ENVOY_LOG_MISC(error, "Fail to apply pre-bind options");
     154           0 :     return connection_socket;
     155           0 :   }
     156         515 :   connection_socket->bind(local_addr);
     157         515 :   ASSERT(local_addr->ip());
     158         515 :   local_addr = connection_socket->connectionInfoProvider().localAddress();
     159         515 :   if (!Network::Socket::applyOptions(connection_socket->options(), *connection_socket,
     160         515 :                                      envoy::config::core::v3::SocketOption::STATE_BOUND)) {
     161           0 :     ENVOY_LOG_MISC(error, "Fail to apply post-bind options");
     162           0 :     connection_socket->close();
     163           0 :   }
     164         515 :   return connection_socket;
     165         515 : }
     166             : 
     167             : bssl::UniquePtr<X509> parseDERCertificate(const std::string& der_bytes,
     168           0 :                                           std::string* error_details) {
     169           0 :   const uint8_t* data;
     170           0 :   const uint8_t* orig_data;
     171           0 :   orig_data = data = reinterpret_cast<const uint8_t*>(der_bytes.data());
     172           0 :   bssl::UniquePtr<X509> cert(d2i_X509(nullptr, &data, der_bytes.size()));
     173           0 :   if (!cert.get()) {
     174           0 :     *error_details = "d2i_X509: fail to parse DER";
     175           0 :     return nullptr;
     176           0 :   }
     177           0 :   if (data < orig_data || static_cast<size_t>(data - orig_data) != der_bytes.size()) {
     178           0 :     *error_details = "There is trailing garbage in DER.";
     179           0 :     return nullptr;
     180           0 :   }
     181           0 :   return cert;
     182           0 : }
     183             : 
     184           0 : int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::string* error_details) {
     185           0 :   int sign_alg = 0;
     186           0 :   if (public_key == nullptr) {
     187           0 :     *error_details = "Invalid leaf cert, bad public key";
     188           0 :     return sign_alg;
     189           0 :   }
     190           0 :   const int pkey_id = EVP_PKEY_id(public_key);
     191           0 :   switch (pkey_id) {
     192           0 :   case EVP_PKEY_EC: {
     193             :     // We only support P-256 ECDSA today.
     194           0 :     const EC_KEY* ecdsa_public_key = EVP_PKEY_get0_EC_KEY(public_key);
     195             :     // Since we checked the key type above, this should be valid.
     196           0 :     ASSERT(ecdsa_public_key != nullptr);
     197           0 :     const EC_GROUP* ecdsa_group = EC_KEY_get0_group(ecdsa_public_key);
     198           0 :     if (ecdsa_group == nullptr || EC_GROUP_get_curve_name(ecdsa_group) != NID_X9_62_prime256v1) {
     199           0 :       *error_details = "Invalid leaf cert, only P-256 ECDSA certificates are supported";
     200           0 :       break;
     201           0 :     }
     202             :     // QUICHE uses SHA-256 as hash function in cert signature.
     203           0 :     sign_alg = SSL_SIGN_ECDSA_SECP256R1_SHA256;
     204           0 :   } break;
     205           0 :   case EVP_PKEY_RSA: {
     206             :     // We require RSA certificates with 2048-bit or larger keys.
     207           0 :     const RSA* rsa_public_key = EVP_PKEY_get0_RSA(public_key);
     208             :     // Since we checked the key type above, this should be valid.
     209           0 :     ASSERT(rsa_public_key != nullptr);
     210           0 :     const unsigned rsa_key_length = RSA_size(rsa_public_key);
     211             : #ifdef BORINGSSL_FIPS
     212             :     if (rsa_key_length != 2048 / 8 && rsa_key_length != 3072 / 8 && rsa_key_length != 4096 / 8) {
     213             :       *error_details = "Invalid leaf cert, only RSA certificates with 2048-bit, 3072-bit or "
     214             :                        "4096-bit keys are supported in FIPS mode";
     215             :       break;
     216             :     }
     217             : #else
     218           0 :     if (rsa_key_length < 2048 / 8) {
     219           0 :       *error_details =
     220           0 :           "Invalid leaf cert, only RSA certificates with 2048-bit or larger keys are supported";
     221           0 :       break;
     222           0 :     }
     223           0 : #endif
     224           0 :     sign_alg = SSL_SIGN_RSA_PSS_RSAE_SHA256;
     225           0 :   } break;
     226           0 :   default:
     227           0 :     *error_details = "Invalid leaf cert, only RSA and ECDSA certificates are supported";
     228           0 :   }
     229           0 :   return sign_alg;
     230           0 : }
     231             : 
     232             : Network::ConnectionSocketPtr
     233             : createServerConnectionSocket(Network::IoHandle& io_handle,
     234             :                              const quic::QuicSocketAddress& self_address,
     235             :                              const quic::QuicSocketAddress& peer_address,
     236           0 :                              const std::string& hostname, absl::string_view alpn) {
     237           0 :   auto connection_socket = std::make_unique<Network::ConnectionSocketImpl>(
     238           0 :       std::make_unique<QuicIoHandleWrapper>(io_handle),
     239           0 :       quicAddressToEnvoyAddressInstance(self_address),
     240           0 :       quicAddressToEnvoyAddressInstance(peer_address));
     241           0 :   connection_socket->setDetectedTransportProtocol("quic");
     242           0 :   connection_socket->setRequestedServerName(hostname);
     243           0 :   connection_socket->setRequestedApplicationProtocols({alpn});
     244           0 :   return connection_socket;
     245           0 : }
     246             : 
     247             : void convertQuicConfig(const envoy::config::core::v3::QuicProtocolOptions& config,
     248           0 :                        quic::QuicConfig& quic_config) {
     249           0 :   int32_t max_streams = PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_concurrent_streams, 100);
     250           0 :   quic_config.SetMaxBidirectionalStreamsToSend(max_streams);
     251           0 :   quic_config.SetMaxUnidirectionalStreamsToSend(max_streams);
     252           0 :   configQuicInitialFlowControlWindow(config, quic_config);
     253           0 :   quic_config.SetConnectionOptionsToSend(quic::ParseQuicTagVector(config.connection_options()));
     254           0 :   quic_config.SetClientConnectionOptions(
     255           0 :       quic::ParseQuicTagVector(config.client_connection_options()));
     256           0 : }
     257             : 
     258             : void configQuicInitialFlowControlWindow(const envoy::config::core::v3::QuicProtocolOptions& config,
     259           0 :                                         quic::QuicConfig& quic_config) {
     260           0 :   size_t stream_flow_control_window_to_send = PROTOBUF_GET_WRAPPED_OR_DEFAULT(
     261           0 :       config, initial_stream_window_size,
     262           0 :       Http3::Utility::OptionsLimits::DEFAULT_INITIAL_STREAM_WINDOW_SIZE);
     263           0 :   if (stream_flow_control_window_to_send < quic::kMinimumFlowControlSendWindow) {
     264             :     // If the configured value is smaller than 16kB, only use it for IETF QUIC, because Google QUIC
     265             :     // requires minimum 16kB stream flow control window. The QUICHE default 16kB will be used for
     266             :     // Google QUIC connections.
     267           0 :     quic_config.SetInitialMaxStreamDataBytesIncomingBidirectionalToSend(
     268           0 :         stream_flow_control_window_to_send);
     269           0 :   } else {
     270             :     // Both Google QUIC and IETF Quic can be configured from this.
     271           0 :     quic_config.SetInitialStreamFlowControlWindowToSend(stream_flow_control_window_to_send);
     272           0 :   }
     273             : 
     274           0 :   uint32_t session_flow_control_window_to_send = PROTOBUF_GET_WRAPPED_OR_DEFAULT(
     275           0 :       config, initial_connection_window_size,
     276           0 :       Http3::Utility::OptionsLimits::DEFAULT_INITIAL_CONNECTION_WINDOW_SIZE);
     277             :   // Config connection level flow control window shouldn't be smaller than the minimum flow control
     278             :   // window supported in QUICHE which is 16kB.
     279           0 :   quic_config.SetInitialSessionFlowControlWindowToSend(
     280           0 :       std::max(quic::kMinimumFlowControlSendWindow,
     281           0 :                static_cast<quic::QuicByteCount>(session_flow_control_window_to_send)));
     282           0 : }
     283             : 
     284             : void adjustNewConnectionIdForRouting(quic::QuicConnectionId& new_connection_id,
     285           0 :                                      const quic::QuicConnectionId& old_connection_id) {
     286           0 :   char* new_connection_id_data = new_connection_id.mutable_data();
     287           0 :   const char* old_connection_id_ptr = old_connection_id.data();
     288             :   // Override the first 4 bytes of the new CID to the original CID's first 4 bytes.
     289           0 :   memcpy(new_connection_id_data, old_connection_id_ptr, 4); // NOLINT(safe-memcpy)
     290           0 : }
     291             : 
     292             : } // namespace Quic
     293             : } // namespace Envoy

Generated by: LCOV version 1.15