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