/* * 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 #include #include #include #include #include #include #include #include namespace quic { std::unique_ptr undoAllClientStateForRetry( std::unique_ptr conn) { // Create a new connection state and copy over properties that don't change // across stateless retry. auto newConn = std::make_unique( 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(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(kPartialReliabilityParameterId), serverParams.parameters); auto activeConnectionIdLimit = getIntegerParameter( TransportParameterId::active_connection_id_limit, serverParams.parameters); // TODO Validate active_connection_id_limit if (!packetSize || *packetSize == 0) { packetSize = kDefaultMaxUDPPayload; } if (*packetSize < kMinMaxUDPPayload) { throw QuicTransportException( folly::to( "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)); 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) { conn.udpSendPacketLen = std::min(*packetSize, kDefaultMaxUDPPayload); } // 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