quic/state/SimpleFrameFunctions.cpp (230 lines of code) (raw):

/* * 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. */ #include <quic/QuicConstants.h> #include <quic/state/QuicStateFunctions.h> #include <quic/state/QuicStreamFunctions.h> #include <quic/state/SimpleFrameFunctions.h> #include <quic/state/stream/StreamSendHandlers.h> namespace quic { void sendSimpleFrame(QuicConnectionStateBase& conn, QuicSimpleFrame frame) { conn.pendingEvents.frames.emplace_back(std::move(frame)); } folly::Optional<QuicSimpleFrame> updateSimpleFrameOnPacketClone( QuicConnectionStateBase& conn, const QuicSimpleFrame& frame) { switch (frame.type()) { case QuicSimpleFrame::Type::StopSendingFrame: if (!conn.streamManager->streamExists( frame.asStopSendingFrame()->streamId)) { return folly::none; } return QuicSimpleFrame(frame); case QuicSimpleFrame::Type::PathChallengeFrame: // Path validation timer expired, path validation failed; // or a different path validation was scheduled if (!conn.outstandingPathValidation || *frame.asPathChallengeFrame() != *conn.outstandingPathValidation) { return folly::none; } return QuicSimpleFrame(frame); case QuicSimpleFrame::Type::PathResponseFrame: // Do not clone PATH_RESPONSE to avoid buffering return folly::none; case QuicSimpleFrame::Type::NewConnectionIdFrame: case QuicSimpleFrame::Type::MaxStreamsFrame: case QuicSimpleFrame::Type::HandshakeDoneFrame: case QuicSimpleFrame::Type::KnobFrame: case QuicSimpleFrame::Type::AckFrequencyFrame: case QuicSimpleFrame::Type::RetireConnectionIdFrame: case QuicSimpleFrame::Type::NewTokenFrame: // TODO junqiw return QuicSimpleFrame(frame); } folly::assume_unreachable(); } void updateSimpleFrameOnPacketSent( QuicConnectionStateBase& conn, const QuicSimpleFrame& simpleFrame) { switch (simpleFrame.type()) { case QuicSimpleFrame::Type::PathChallengeFrame: conn.outstandingPathValidation = std::move(conn.pendingEvents.pathChallenge); conn.pendingEvents.schedulePathValidationTimeout = true; // Start the clock to measure Rtt conn.pathChallengeStartTime = Clock::now(); break; default: { auto& frames = conn.pendingEvents.frames; auto itr = std::find(frames.begin(), frames.end(), simpleFrame); CHECK(itr != frames.end()); frames.erase(itr); break; } } } void updateSimpleFrameOnPacketLoss( QuicConnectionStateBase& conn, const QuicSimpleFrame& frame) { switch (frame.type()) { case QuicSimpleFrame::Type::StopSendingFrame: { const StopSendingFrame& stopSendingFrame = *frame.asStopSendingFrame(); if (conn.streamManager->streamExists(stopSendingFrame.streamId)) { conn.pendingEvents.frames.push_back(stopSendingFrame); } break; } case QuicSimpleFrame::Type::PathChallengeFrame: { const PathChallengeFrame& pathChallenge = *frame.asPathChallengeFrame(); if (conn.outstandingPathValidation && pathChallenge == *conn.outstandingPathValidation) { conn.pendingEvents.pathChallenge = pathChallenge; } break; } case QuicSimpleFrame::Type::PathResponseFrame: { // Do not retransmit PATH_RESPONSE to avoid buffering break; } case QuicSimpleFrame::Type::HandshakeDoneFrame: { const auto& handshakeDoneFrame = *frame.asHandshakeDoneFrame(); conn.pendingEvents.frames.push_back(handshakeDoneFrame); break; } case QuicSimpleFrame::Type::NewConnectionIdFrame: case QuicSimpleFrame::Type::MaxStreamsFrame: case QuicSimpleFrame::Type::RetireConnectionIdFrame: case QuicSimpleFrame::Type::KnobFrame: case QuicSimpleFrame::Type::AckFrequencyFrame: case QuicSimpleFrame::Type::NewTokenFrame: conn.pendingEvents.frames.push_back(frame); break; } } bool updateSimpleFrameOnPacketReceived( QuicConnectionStateBase& conn, const QuicSimpleFrame& frame, PacketNum /*packetNum*/, bool fromChangedPeerAddress) { switch (frame.type()) { case QuicSimpleFrame::Type::StopSendingFrame: { const StopSendingFrame& stopSending = *frame.asStopSendingFrame(); auto stream = conn.streamManager->getStream(stopSending.streamId); if (stream) { sendStopSendingSMHandler(*stream, stopSending); } return true; } case QuicSimpleFrame::Type::PathChallengeFrame: { bool rotatedId = conn.retireAndSwitchPeerConnectionIds(); if (!rotatedId) { throw QuicTransportException( "No more connection ids to use for new path.", TransportErrorCode::INVALID_MIGRATION); } const PathChallengeFrame& pathChallenge = *frame.asPathChallengeFrame(); conn.pendingEvents.frames.emplace_back( PathResponseFrame(pathChallenge.pathData)); return false; } case QuicSimpleFrame::Type::PathResponseFrame: { const PathResponseFrame& pathResponse = *frame.asPathResponseFrame(); // Ignore the response if outstandingPathValidation is none or // the path data doesn't match what's in outstandingPathValidation if (fromChangedPeerAddress || !conn.outstandingPathValidation || pathResponse.pathData != conn.outstandingPathValidation->pathData) { return false; } if (conn.qLogger) { conn.qLogger->addPathValidationEvent(true); } // TODO update source token, conn.outstandingPathValidation = folly::none; conn.pendingEvents.schedulePathValidationTimeout = false; // stop the clock to measure init rtt std::chrono::microseconds sampleRtt = std::chrono::duration_cast<std::chrono::microseconds>( Clock::now() - conn.pathChallengeStartTime); updateRtt(conn, sampleRtt, 0us); return false; } case QuicSimpleFrame::Type::NewConnectionIdFrame: { const NewConnectionIdFrame& newConnectionId = *frame.asNewConnectionIdFrame(); // TODO vchynaro Ensure we ignore smaller subsequent retirePriorTos // than the largest seen so far. if (newConnectionId.retirePriorTo > newConnectionId.sequenceNumber) { throw QuicTransportException( "Retire prior to greater than sequence number", TransportErrorCode::PROTOCOL_VIOLATION); } for (const auto& existingPeerConnIdData : conn.peerConnectionIds) { if (existingPeerConnIdData.connId == newConnectionId.connectionId) { if (existingPeerConnIdData.sequenceNumber != newConnectionId.sequenceNumber) { throw QuicTransportException( "Repeated connection id with different sequence number.", TransportErrorCode::PROTOCOL_VIOLATION); } else { // No-op on repeated conn id. return false; } } } // PeerConnectionIds holds ALL peer's connection ids // (initial + NEW_CONNECTION_ID). // If using 0-len peer cid then this would be the only element. auto peerConnId = (conn.nodeType == QuicNodeType::Client ? conn.serverConnectionId : conn.clientConnectionId); if (!peerConnId || peerConnId->size() == 0) { throw QuicTransportException( "Endpoint is already using 0-len connection ids.", TransportErrorCode::PROTOCOL_VIOLATION); } // TODO vchynaro Implement retire_prior_to logic // selfActiveConnectionIdLimit represents the active_connection_id_limit // transport parameter which is the maximum amount of connection ids // provided by NEW_CONNECTION_ID frames. We add 1 to represent the initial // cid. if (conn.peerConnectionIds.size() == conn.transportSettings.selfActiveConnectionIdLimit + 1) { // Unspec'd as of d-23 if a server doesn't respect the // active_connection_id_limit. Ignore frame. return false; } conn.peerConnectionIds.emplace_back( newConnectionId.connectionId, newConnectionId.sequenceNumber, newConnectionId.token); return false; } case QuicSimpleFrame::Type::MaxStreamsFrame: { const MaxStreamsFrame& maxStreamsFrame = *frame.asMaxStreamsFrame(); if (maxStreamsFrame.isForBidirectionalStream()) { conn.streamManager->setMaxLocalBidirectionalStreams( maxStreamsFrame.maxStreams); } else { conn.streamManager->setMaxLocalUnidirectionalStreams( maxStreamsFrame.maxStreams); } return true; } case QuicSimpleFrame::Type::RetireConnectionIdFrame: { return true; } case QuicSimpleFrame::Type::HandshakeDoneFrame: { if (conn.nodeType == QuicNodeType::Server) { throw QuicTransportException( "Received HANDSHAKE_DONE from client.", TransportErrorCode::PROTOCOL_VIOLATION, FrameType::HANDSHAKE_DONE); } // Mark the handshake confirmed in the handshake layer before doing // any dropping, as this gives us a chance to process ACKs in this // packet. conn.handshakeLayer->handshakeConfirmed(); return true; } case QuicSimpleFrame::Type::KnobFrame: { const KnobFrame& knobFrame = *frame.asKnobFrame(); conn.pendingEvents.knobs.emplace_back( knobFrame.knobSpace, knobFrame.id, knobFrame.blob->clone()); return true; } case QuicSimpleFrame::Type::AckFrequencyFrame: { if (!conn.transportSettings.minAckDelay.hasValue()) { return true; } const auto ackFrequencyFrame = frame.asAckFrequencyFrame(); auto& ackState = conn.ackStates.appDataAckState; if (!ackState.ackFrequencySequenceNumber || ackFrequencyFrame->sequenceNumber > ackState.ackFrequencySequenceNumber.value()) { ackState.tolerance = ackFrequencyFrame->packetTolerance; ackState.ignoreReorder = ackFrequencyFrame->ignoreOrder; conn.ackStates.maxAckDelay = std::chrono::microseconds(std::max<uint64_t>( conn.transportSettings.minAckDelay->count(), ackFrequencyFrame->updateMaxAckDelay)); } return true; } case QuicSimpleFrame::Type::NewTokenFrame: { // TODO: client impl return true; } } folly::assume_unreachable(); } } // namespace quic