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  }