1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-24 04:01:07 +03:00
Files
mvfst/quic/client/state/ClientStateMachine.h
Joseph Beshay b45c82b884 ACK_EXTENDED frame support
Summary:
This introduces a new frame type for acks (ACK_EXTENDED) that can carry optional fields depending on the features supported by the peer. The currently supported features set will include ECN count fields, and Receive Timstamp fields. This enables a quic connection to report both ECN counts and receive timestamps, which is not possible otherwise because they use different frame types.

Support for the extended ack as well as the set of features that can be included in it is negotiated through a new transport parameter (extended_ack_supported = 0xff0a004). Its value indicates which features are supported by the local transport. The value is an integer which is evaluated against the following bitmasks:
```
  ECN_COUNTS = 0x01,
  RECEIVE_TIMESTAMPS = 0x02,
```

This diff introduces the transport parameter and negotiates the supported features between the peers of the connection. The parameter is cached in the psk cache so the client can remember the server config. It is also encoded inside the 0-rtt ticket so the server can reject it if its local config has changed.

The following diffs add reading and writing the frame itself.

The ACK_EXTENDED frame itself will have the following format
```
ACK_EXTENDED Frame {
  Type (i) = 0xB1
  // Fields of the existing ACK (type=0x02) frame:
  Largest Acknowledged (i),
  ACK Delay (i),
  ACK Range Count (i),
  First ACK Range (i),
  ACK Range (..) ...,
  Extended Ack Features (i),
  // Optional ECN counts (if bit 0 is set in Features)
  [ECN Counts (..)],
  // Optional Receive Timestamps (if bit 1 is set in Features)
  [Receive Timestamps (..)]
}

// Fields from the existing ACK_ECN frame
ECN Counts {
  ECT0 Count (i),
  ECT1 Count (i),
  ECN-CE Count (i),
}

// Fields from the existing ACK_RECEIVE_TIMESTAMPS frame
Receive Timestamps {
  Timestamp Range Count (i),
  Timestamp Ranges (..) ...,
}

Timestamp Range {
  Gap (i),
  Timestamp Delta Count (i),
  Timestamp Delta (i) ...,
}
```

Reviewed By: sharmafb

Differential Revision: D68931151

fbshipit-source-id: 44c8c83d2f434abca97c4e85f0fa7502736cddc1
2025-02-24 12:32:50 -08:00

172 lines
6.3 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/udpsocket/QuicAsyncUDPSocket.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 {
ReceivedUdpPacket udpPacket;
folly::SocketAddress peer;
PendingClientData(ReceivedUdpPacket 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.
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.
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.
Optional<ConnectionId> originalDestinationConnectionId;
std::shared_ptr<ClientHandshakeFactory> handshakeFactory;
ClientHandshake* clientHandshakeLayer;
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<QuicAsyncUDPSocket> 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
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,
bool peerAdvertisedAckReceiveTimestampsEnabled,
uint64_t peerAdvertisedMaxRecvTimestampsPerAck,
uint64_t peerAdvertisedReceiveTimestampsExponent,
bool peerAdvertisedReliableStreamResetSupport,
ExtendedAckFeatureMaskType peerAdvertisedExtendedAckSupport);
CachedServerTransportParameters getServerCachedTransportParameters(
const QuicClientConnectionState& conn);
void updateTransportParamsFromCachedEarlyParams(
QuicClientConnectionState& conn,
const CachedServerTransportParameters& transportParams);
} // namespace quic