mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-11-24 04:01:07 +03:00
Summary: This diff drops `NetworkDataSingle` in favor of `ReceivedPacket`. The latter contains a `ReceivedPacket::Timings` field that has the same `receiveTimePoint` currently in `NetworkDataSingle`, while also providing other useful signals. -- This diff is part of a larger stack focused on the following: - **Cleaning up client and server UDP packet receive paths while improving testability.** We currently have multiple receive paths for client and server. Capabilities vary significantly and there are few tests. For instance: - The server receive path supports socket RX timestamps, abet incorrectly in that it does not store timestamp per packet. In comparison, the client receive path does not currently support socket RX timestamps, although the code in `QuicClientTransport::recvmsg` and `QuicClientTransport::recvmmsg` makes reference to socket RX timestamps, making it confusing to understand the capabilities available when tracing through the code. This complicates the tests in `QuicTypedTransportTests`, as we have to disable test logic that depends on socket RX timestamps for client tests. - The client currently has three receive paths, and none of them are well tested. - **Modularize and abstract components in the receive path.** This will make it easier to mock/fake the UDP socket and network layers. - `QuicClientTransport` and `QuicServerTransport` currently contain UDP socket handling logic that operates over lower layer primitives such `cmsg` and `io_vec` (see `QuicClientTransport::recvmmsg` and `...::recvmsg` as examples). - Because this UDP socket handling logic is inside of the mvfst transport implementations, it is difficult to test this logic in isolation and mock/fake the underlying socket and network layers. For instance, injecting a user space network emulator that operates at the socket layer would require faking `folly::AsyncUDPSocket`, which is non-trivial given that `AsyncUDPSocket` does not abstract away intricacies arising from the aforementioned lower layer primitives. - By shifting this logic into an intermediate layer between the transport and the underlying UDP socket, it will be easier to mock out the UDP socket layer when testing functionality at higher layers, and inject fake components when we want to emulate the network between a mvfst client and server. It will also be easier for us to have unit tests focused on testing interactions between the UDP socket implementation and this intermediate layer. - **Improving receive path timestamping.** We only record a single timestamp per `NetworkData` at the moment, but (1) it is possible for a `NetworkData` to have multiple packets, each with their own timestamps, and (2) we should be able to record both userspace and socket timestamps Reviewed By: silver23arrow Differential Revision: D48739219 fbshipit-source-id: fc2cdb7b425d68c729dd3bec00b6c6ff3c4bf8ec
167 lines
6.1 KiB
C++
167 lines
6.1 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <quic/client/handshake/ClientHandshake.h>
|
|
#include <quic/client/handshake/ClientHandshakeFactory.h>
|
|
#include <quic/common/NetworkData.h>
|
|
#include <quic/common/QuicAsyncUDPSocketWrapper.h>
|
|
#include <quic/congestion_control/QuicCubic.h>
|
|
#include <quic/flowcontrol/QuicFlowController.h>
|
|
#include <quic/handshake/TransportParameters.h>
|
|
#include <quic/state/QuicStateFunctions.h>
|
|
#include <quic/state/StateData.h>
|
|
|
|
namespace quic {
|
|
|
|
struct CachedServerTransportParameters;
|
|
|
|
struct PendingClientData {
|
|
ReceivedPacket udpPacket;
|
|
folly::SocketAddress peer;
|
|
|
|
PendingClientData(ReceivedPacket udpPacketIn, folly::SocketAddress peerIn)
|
|
: udpPacket(std::move(udpPacketIn)), peer(std::move(peerIn)) {}
|
|
};
|
|
|
|
struct QuicClientConnectionState : public QuicConnectionStateBase {
|
|
~QuicClientConnectionState() override = default;
|
|
|
|
// Zero rtt write header cipher.
|
|
std::unique_ptr<PacketNumberCipher> zeroRttWriteHeaderCipher;
|
|
// Write cipher for 0-RTT data
|
|
std::unique_ptr<Aead> zeroRttWriteCipher;
|
|
|
|
// The stateless reset token sent by the server.
|
|
folly::Optional<StatelessResetToken> statelessResetToken;
|
|
|
|
// The retry token sent by the server.
|
|
std::string retryToken;
|
|
|
|
// The new token that potentially verifies the address of the
|
|
// client.
|
|
std::string newToken;
|
|
|
|
// This is the destination connection id that will be sent in the outgoing
|
|
// client initial packet. It is modified in the event of a retry.
|
|
folly::Optional<ConnectionId> initialDestinationConnectionId;
|
|
|
|
// This is the original destination connection id. It is the same as the
|
|
// initialDestinationConnectionId when there is no retry involved. When
|
|
// there is retry involved, this is the value of the destination connection
|
|
// id sent in the very first initial packet.
|
|
folly::Optional<ConnectionId> originalDestinationConnectionId;
|
|
|
|
std::shared_ptr<ClientHandshakeFactory> handshakeFactory;
|
|
ClientHandshake* clientHandshakeLayer;
|
|
|
|
folly::Optional<TimePoint> lastCloseSentTime;
|
|
|
|
// Save the server transport params here so that client can access the value
|
|
// when it wants to write the values to psk cache
|
|
// TODO Save TicketTransportParams here instead of in QuicClientTransport
|
|
bool serverInitialParamsSet_{false};
|
|
uint64_t peerAdvertisedInitialMaxData{0};
|
|
uint64_t peerAdvertisedInitialMaxStreamDataBidiLocal{0};
|
|
uint64_t peerAdvertisedInitialMaxStreamDataBidiRemote{0};
|
|
uint64_t peerAdvertisedInitialMaxStreamDataUni{0};
|
|
uint64_t peerAdvertisedInitialMaxStreamsBidi{0};
|
|
uint64_t peerAdvertisedInitialMaxStreamsUni{0};
|
|
|
|
struct HappyEyeballsState {
|
|
// Delay timer
|
|
QuicTimerCallback* connAttemptDelayTimeout{nullptr};
|
|
|
|
// IPv6 peer address
|
|
folly::SocketAddress v6PeerAddress;
|
|
|
|
// IPv4 peer address
|
|
folly::SocketAddress v4PeerAddress;
|
|
|
|
// The address that this socket will try to connect to after connection
|
|
// attempt delay timeout fires
|
|
folly::SocketAddress secondPeerAddress;
|
|
|
|
// The UDP socket that will be used for the second connection attempt
|
|
std::unique_ptr<QuicAsyncUDPSocketWrapper> secondSocket;
|
|
|
|
// Whether should write to the first UDP socket
|
|
bool shouldWriteToFirstSocket{true};
|
|
|
|
// Whether should write to the second UDP socket
|
|
bool shouldWriteToSecondSocket{false};
|
|
|
|
// Whether HappyEyeballs has finished
|
|
// The signal of finishing is first successful decryption of a packet
|
|
bool finished{false};
|
|
};
|
|
|
|
HappyEyeballsState happyEyeballsState;
|
|
|
|
// Short header packets we received but couldn't yet decrypt.
|
|
std::vector<PendingClientData> pendingOneRttData;
|
|
// Handshake packets we received but couldn't yet decrypt.
|
|
std::vector<PendingClientData> pendingHandshakeData;
|
|
|
|
// Whether 0-rtt has been rejected in this connection.
|
|
// The value should be set after the handshake if 0-rtt was attempted
|
|
folly::Optional<bool> zeroRttRejected;
|
|
|
|
explicit QuicClientConnectionState(
|
|
std::shared_ptr<ClientHandshakeFactory> handshakeFactoryIn)
|
|
: QuicConnectionStateBase(QuicNodeType::Client),
|
|
handshakeFactory(std::move(handshakeFactoryIn)) {
|
|
cryptoState = std::make_unique<QuicCryptoState>();
|
|
congestionController = std::make_unique<Cubic>(*this);
|
|
connectionTime = Clock::now();
|
|
originalVersion = QuicVersion::MVFST;
|
|
DCHECK(handshakeFactory);
|
|
auto tmpClientHandshake =
|
|
std::move(*handshakeFactory).makeClientHandshake(this);
|
|
clientHandshakeLayer = tmpClientHandshake.get();
|
|
handshakeLayer = std::move(tmpClientHandshake);
|
|
// We shouldn't normally need to set this until we're starting the
|
|
// transport, however writing unit tests is much easier if we set this here.
|
|
updateFlowControlStateWithSettings(flowControlState, transportSettings);
|
|
streamManager = std::make_unique<QuicStreamManager>(
|
|
*this, this->nodeType, transportSettings);
|
|
transportSettings.selfActiveConnectionIdLimit =
|
|
kDefaultActiveConnectionIdLimit;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Undos the clients state to be the original state of the client.
|
|
*/
|
|
std::unique_ptr<QuicClientConnectionState> undoAllClientStateForRetry(
|
|
std::unique_ptr<QuicClientConnectionState> conn);
|
|
|
|
void processServerInitialParams(
|
|
QuicClientConnectionState& conn,
|
|
const ServerTransportParameters& serverParams,
|
|
PacketNum packetNum);
|
|
|
|
void cacheServerInitialParams(
|
|
QuicClientConnectionState& conn,
|
|
uint64_t peerAdvertisedInitialMaxData,
|
|
uint64_t peerAdvertisedInitialMaxStreamDataBidiLocal,
|
|
uint64_t peerAdvertisedInitialMaxStreamDataBidiRemote,
|
|
uint64_t peerAdvertisedInitialMaxStreamDataUni,
|
|
uint64_t peerAdvertisedInitialMaxStreamsBidi,
|
|
uint64_t peerAdvertisedInitialMaxStreamUni,
|
|
bool peerAdvertisedKnobFrameSupport);
|
|
|
|
CachedServerTransportParameters getServerCachedTransportParameters(
|
|
const QuicClientConnectionState& conn);
|
|
|
|
void updateTransportParamsFromCachedEarlyParams(
|
|
QuicClientConnectionState& conn,
|
|
const CachedServerTransportParameters& transportParams);
|
|
|
|
} // namespace quic
|