Source file src/vendor/golang.org/x/net/quic/path.go
1 // Copyright 2024 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package quic 6 7 import "time" 8 9 type pathState struct { 10 // Response to a peer's PATH_CHALLENGE. 11 // This is not a sentVal, because we don't resend lost PATH_RESPONSE frames. 12 // We only track the most recent PATH_CHALLENGE. 13 // If the peer sends a second PATH_CHALLENGE before we respond to the first, 14 // we'll drop the first response. 15 sendPathResponse pathResponseType 16 data pathChallengeData 17 } 18 19 // pathChallengeData is data carried in a PATH_CHALLENGE or PATH_RESPONSE frame. 20 type pathChallengeData [64 / 8]byte 21 22 type pathResponseType uint8 23 24 const ( 25 pathResponseNotNeeded = pathResponseType(iota) 26 pathResponseSmall // send PATH_RESPONSE, do not expand datagram 27 pathResponseExpanded // send PATH_RESPONSE, expand datagram to 1200 bytes 28 ) 29 30 func (c *Conn) handlePathChallenge(_ time.Time, dgram *datagram, data pathChallengeData) { 31 // A PATH_RESPONSE is sent in a datagram expanded to 1200 bytes, 32 // except when this would exceed the anti-amplification limit. 33 // 34 // Rather than maintaining anti-amplification state for each path 35 // we may be sending a PATH_RESPONSE on, follow the following heuristic: 36 // 37 // If we receive a PATH_CHALLENGE in an expanded datagram, 38 // respond with an expanded datagram. 39 // 40 // If we receive a PATH_CHALLENGE in a non-expanded datagram, 41 // then the peer is presumably blocked by its own anti-amplification limit. 42 // Respond with a non-expanded datagram. Receiving this PATH_RESPONSE 43 // will validate the path to the peer, remove its anti-amplification limit, 44 // and permit it to send a followup PATH_CHALLENGE in an expanded datagram. 45 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.2.1 46 if len(dgram.b) >= smallestMaxDatagramSize { 47 c.path.sendPathResponse = pathResponseExpanded 48 } else { 49 c.path.sendPathResponse = pathResponseSmall 50 } 51 c.path.data = data 52 } 53 54 func (c *Conn) handlePathResponse(now time.Time, _ pathChallengeData) { 55 // "If the content of a PATH_RESPONSE frame does not match the content of 56 // a PATH_CHALLENGE frame previously sent by the endpoint, 57 // the endpoint MAY generate a connection error of type PROTOCOL_VIOLATION." 58 // https://www.rfc-editor.org/rfc/rfc9000.html#section-19.18-4 59 // 60 // We never send PATH_CHALLENGE frames. 61 c.abort(now, localTransportError{ 62 code: errProtocolViolation, 63 reason: "PATH_RESPONSE received when no PATH_CHALLENGE sent", 64 }) 65 } 66 67 // appendPathFrames appends path validation related frames to the current packet. 68 // If the return value pad is true, then the packet should be padded to 1200 bytes. 69 func (c *Conn) appendPathFrames() (pad, ok bool) { 70 if c.path.sendPathResponse == pathResponseNotNeeded { 71 return pad, true 72 } 73 // We're required to send the PATH_RESPONSE on the path where the 74 // PATH_CHALLENGE was received (RFC 9000, Section 8.2.2). 75 // 76 // At the moment, we don't support path migration and reject packets if 77 // the peer changes its source address, so just sending the PATH_RESPONSE 78 // in a regular datagram is fine. 79 if !c.w.appendPathResponseFrame(c.path.data) { 80 return pad, false 81 } 82 if c.path.sendPathResponse == pathResponseExpanded { 83 pad = true 84 } 85 c.path.sendPathResponse = pathResponseNotNeeded 86 return pad, true 87 } 88