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.cpp
Matt Joras 42bba01005 Draft-29 support.
Summary:
This implements the connection ID validation via transport parameters. Note we don't do anything with the retry transport parameter yet.

This will probably require further surgery to tests when we want the MVFST version to do this, but for now I'm punting on that test plumbing.

This retains support for h3-27.

Reviewed By: yangchi

Differential Revision: D22045631

fbshipit-source-id: e93841e734c0683655c751d808fd90b3b391eb3e
2020-06-16 17:05:41 -07:00

254 lines
11 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#include <quic/client/state/ClientStateMachine.h>
#include <folly/io/async/AsyncSocketException.h>
#include <quic/client/handshake/CachedServerTransportParameters.h>
#include <quic/common/TimeUtil.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/QuicStreamUtilities.h>
#include <quic/state/StateData.h>
namespace quic {
std::unique_ptr<QuicClientConnectionState> undoAllClientStateForRetry(
std::unique_ptr<QuicClientConnectionState> conn) {
// Create a new connection state and copy over properties that don't change
// across stateless retry.
auto newConn = std::make_unique<QuicClientConnectionState>(
std::move(conn->handshakeFactory));
newConn->qLogger = conn->qLogger;
newConn->clientConnectionId = conn->clientConnectionId;
newConn->initialDestinationConnectionId =
conn->initialDestinationConnectionId;
// TODO: don't carry server connection id over to the new connection.
newConn->serverConnectionId = conn->serverConnectionId;
newConn->ackStates.initialAckState.nextPacketNum =
conn->ackStates.initialAckState.nextPacketNum;
newConn->ackStates.handshakeAckState.nextPacketNum =
conn->ackStates.handshakeAckState.nextPacketNum;
newConn->ackStates.appDataAckState.nextPacketNum =
conn->ackStates.appDataAckState.nextPacketNum;
newConn->version = conn->version;
newConn->originalVersion = conn->originalVersion;
newConn->originalPeerAddress = conn->originalPeerAddress;
newConn->peerAddress = conn->peerAddress;
newConn->udpSendPacketLen = conn->udpSendPacketLen;
newConn->supportedVersions = conn->supportedVersions;
newConn->transportSettings = conn->transportSettings;
newConn->initialWriteCipher = std::move(conn->initialWriteCipher);
newConn->readCodec = std::make_unique<QuicReadCodec>(QuicNodeType::Client);
newConn->readCodec->setClientConnectionId(*conn->clientConnectionId);
newConn->readCodec->setCodecParameters(CodecParameters(
conn->peerAckDelayExponent, conn->originalVersion.value()));
newConn->earlyDataAppParamsValidator =
std::move(conn->earlyDataAppParamsValidator);
newConn->earlyDataAppParamsGetter = std::move(conn->earlyDataAppParamsGetter);
return newConn;
}
void processServerInitialParams(
QuicClientConnectionState& conn,
ServerTransportParameters serverParams,
PacketNum packetNum) {
auto maxData = getIntegerParameter(
TransportParameterId::initial_max_data, serverParams.parameters);
auto maxStreamDataBidiLocal = getIntegerParameter(
TransportParameterId::initial_max_stream_data_bidi_local,
serverParams.parameters);
auto maxStreamDataBidiRemote = getIntegerParameter(
TransportParameterId::initial_max_stream_data_bidi_remote,
serverParams.parameters);
auto maxStreamDataUni = getIntegerParameter(
TransportParameterId::initial_max_stream_data_uni,
serverParams.parameters);
auto idleTimeout = getIntegerParameter(
TransportParameterId::idle_timeout, serverParams.parameters);
auto maxStreamsBidi = getIntegerParameter(
TransportParameterId::initial_max_streams_bidi, serverParams.parameters);
auto maxStreamsUni = getIntegerParameter(
TransportParameterId::initial_max_streams_uni, serverParams.parameters);
auto ackDelayExponent = getIntegerParameter(
TransportParameterId::ack_delay_exponent, serverParams.parameters);
auto packetSize = getIntegerParameter(
TransportParameterId::max_packet_size, serverParams.parameters);
auto statelessResetToken =
getStatelessResetTokenParameter(serverParams.parameters);
auto partialReliability = getIntegerParameter(
static_cast<TransportParameterId>(kPartialReliabilityParameterId),
serverParams.parameters);
auto activeConnectionIdLimit = getIntegerParameter(
TransportParameterId::active_connection_id_limit,
serverParams.parameters);
if (conn.version == QuicVersion::QUIC_DRAFT) {
auto initialSourceConnId = getConnIdParameter(
TransportParameterId::initial_source_connection_id,
serverParams.parameters);
auto originalDestinationConnId = getConnIdParameter(
TransportParameterId::original_destination_connection_id,
serverParams.parameters);
if (!initialSourceConnId || !originalDestinationConnId ||
initialSourceConnId.value() !=
conn.readCodec->getServerConnectionId() ||
originalDestinationConnId.value() !=
conn.initialDestinationConnectionId) {
throw QuicTransportException(
"Initial CID does not match.",
TransportErrorCode::TRANSPORT_PARAMETER_ERROR);
}
}
// TODO Validate active_connection_id_limit
if (!packetSize || *packetSize == 0) {
packetSize = kDefaultUDPSendPacketLen;
}
if (*packetSize < kMinMaxUDPPayload) {
throw QuicTransportException(
folly::to<std::string>(
"Max packet size too small. received max_packetSize = ",
*packetSize),
TransportErrorCode::TRANSPORT_PARAMETER_ERROR);
}
VLOG(10) << "Client advertised flow control ";
VLOG(10) << "conn=" << maxData.value_or(0);
VLOG(10) << " stream bidi local=" << maxStreamDataBidiLocal.value_or(0)
<< " ";
VLOG(10) << " stream bidi remote=" << maxStreamDataBidiRemote.value_or(0)
<< " ";
VLOG(10) << " stream uni=" << maxStreamDataUni.value_or(0) << " ";
VLOG(10) << conn;
conn.flowControlState.peerAdvertisedMaxOffset = maxData.value_or(0);
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiLocal =
maxStreamDataBidiLocal.value_or(0);
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiRemote =
maxStreamDataBidiRemote.value_or(0);
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetUni =
maxStreamDataUni.value_or(0);
// TODO Make idleTimeout disableable via transport parameter.
conn.streamManager->setMaxLocalBidirectionalStreams(
maxStreamsBidi.value_or(0));
conn.peerAdvertisedInitialMaxStreamsBidi = maxStreamsBidi.value_or(0);
conn.streamManager->setMaxLocalUnidirectionalStreams(
maxStreamsUni.value_or(0));
conn.peerAdvertisedInitialMaxStreamsUni = maxStreamsUni.value_or(0);
conn.peerIdleTimeout = std::chrono::milliseconds(idleTimeout.value_or(0));
conn.peerIdleTimeout = timeMin(conn.peerIdleTimeout, kMaxIdleTimeout);
if (ackDelayExponent && *ackDelayExponent > kMaxAckDelayExponent) {
throw QuicTransportException(
"ack_delay_exponent too large",
TransportErrorCode::TRANSPORT_PARAMETER_ERROR);
}
conn.peerAckDelayExponent =
ackDelayExponent.value_or(kDefaultAckDelayExponent);
// TODO: udpSendPacketLen should also be limited by PMTU
if (conn.transportSettings.canIgnorePathMTU) {
if (*packetSize > kDefaultMaxUDPPayload) {
*packetSize = kDefaultUDPSendPacketLen;
}
conn.udpSendPacketLen = *packetSize;
}
// Currently no-op for a client; it doesn't issue connection ids
// to the server.
conn.peerActiveConnectionIdLimit =
activeConnectionIdLimit.value_or(kDefaultConnectionIdLimit);
if (partialReliability && *partialReliability != 0 &&
conn.transportSettings.partialReliabilityEnabled) {
conn.partialReliabilityEnabled = true;
}
VLOG(10) << "conn.partialReliabilityEnabled="
<< conn.partialReliabilityEnabled;
conn.statelessResetToken = std::move(statelessResetToken);
// Update the existing streams, because we allow streams to be created before
// the connection is established.
conn.streamManager->streamStateForEach([&conn,
&packetNum](QuicStreamState& s) {
auto windowSize = isUnidirectionalStream(s.id)
? conn.transportSettings.advertisedInitialUniStreamWindowSize
: isLocalStream(conn.nodeType, s.id)
? conn.transportSettings.advertisedInitialBidiLocalStreamWindowSize
: conn.transportSettings
.advertisedInitialBidiRemoteStreamWindowSize;
handleStreamWindowUpdate(s, windowSize, packetNum);
});
}
void cacheServerInitialParams(
QuicClientConnectionState& conn,
uint64_t peerAdvertisedInitialMaxData,
uint64_t peerAdvertisedInitialMaxStreamDataBidiLocal,
uint64_t peerAdvertisedInitialMaxStreamDataBidiRemote,
uint64_t peerAdvertisedInitialMaxStreamDataUni,
uint64_t peerAdvertisedInitialMaxStreamsBidi,
uint64_t peerAdvertisedInitialMaxStreamUni) {
conn.serverInitialParamsSet_ = true;
conn.peerAdvertisedInitialMaxData = peerAdvertisedInitialMaxData;
conn.peerAdvertisedInitialMaxStreamDataBidiLocal =
peerAdvertisedInitialMaxStreamDataBidiLocal;
conn.peerAdvertisedInitialMaxStreamDataBidiRemote =
peerAdvertisedInitialMaxStreamDataBidiRemote;
conn.peerAdvertisedInitialMaxStreamDataUni =
peerAdvertisedInitialMaxStreamDataUni;
conn.peerAdvertisedInitialMaxStreamsBidi =
peerAdvertisedInitialMaxStreamsBidi;
conn.peerAdvertisedInitialMaxStreamsUni = peerAdvertisedInitialMaxStreamUni;
}
CachedServerTransportParameters getServerCachedTransportParameters(
const QuicClientConnectionState& conn) {
DCHECK(conn.serverInitialParamsSet_);
CachedServerTransportParameters transportParams;
transportParams.idleTimeout = conn.peerIdleTimeout.count();
transportParams.maxRecvPacketSize = conn.udpSendPacketLen;
transportParams.initialMaxData = conn.peerAdvertisedInitialMaxData;
transportParams.initialMaxStreamDataBidiLocal =
conn.peerAdvertisedInitialMaxStreamDataBidiLocal;
transportParams.initialMaxStreamDataBidiRemote =
conn.peerAdvertisedInitialMaxStreamDataBidiRemote;
transportParams.initialMaxStreamDataUni =
conn.peerAdvertisedInitialMaxStreamDataUni;
transportParams.initialMaxStreamsBidi =
conn.peerAdvertisedInitialMaxStreamsBidi;
transportParams.initialMaxStreamsUni =
conn.peerAdvertisedInitialMaxStreamsUni;
return transportParams;
}
void updateTransportParamsFromCachedEarlyParams(
QuicClientConnectionState& conn,
const CachedServerTransportParameters& transportParams) {
conn.peerIdleTimeout = std::chrono::milliseconds(transportParams.idleTimeout);
if (conn.transportSettings.canIgnorePathMTU) {
conn.udpSendPacketLen = transportParams.maxRecvPacketSize;
}
conn.flowControlState.peerAdvertisedMaxOffset =
transportParams.initialMaxData;
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiLocal =
transportParams.initialMaxStreamDataBidiLocal;
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetBidiRemote =
transportParams.initialMaxStreamDataBidiRemote;
conn.flowControlState.peerAdvertisedInitialMaxStreamOffsetUni =
transportParams.initialMaxStreamDataUni;
conn.streamManager->setMaxLocalBidirectionalStreams(
transportParams.initialMaxStreamsBidi);
conn.streamManager->setMaxLocalUnidirectionalStreams(
transportParams.initialMaxStreamsUni);
}
} // namespace quic