...

Source file src/golang.org/x/net/internal/quic/conn_send.go

Documentation: golang.org/x/net/internal/quic

     1  // Copyright 2023 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 (
    10  	"crypto/tls"
    11  	"errors"
    12  	"time"
    13  )
    14  
    15  // maybeSend sends datagrams, if possible.
    16  //
    17  // If sending is blocked by pacing, it returns the next time
    18  // a datagram may be sent.
    19  //
    20  // If sending is blocked indefinitely, it returns the zero Time.
    21  func (c *Conn) maybeSend(now time.Time) (next time.Time) {
    22  	// Assumption: The congestion window is not underutilized.
    23  	// If congestion control, pacing, and anti-amplification all permit sending,
    24  	// but we have no packet to send, then we will declare the window underutilized.
    25  	c.loss.cc.setUnderutilized(false)
    26  
    27  	// Send one datagram on each iteration of this loop,
    28  	// until we hit a limit or run out of data to send.
    29  	//
    30  	// For each number space where we have write keys,
    31  	// attempt to construct a packet in that space.
    32  	// If the packet contains no frames (we have no data in need of sending),
    33  	// abandon the packet.
    34  	//
    35  	// Speculatively constructing packets means we don't need
    36  	// separate code paths for "do we have data to send?" and
    37  	// "send the data" that need to be kept in sync.
    38  	for {
    39  		limit, next := c.loss.sendLimit(now)
    40  		if limit == ccBlocked {
    41  			// If anti-amplification blocks sending, then no packet can be sent.
    42  			return next
    43  		}
    44  		if !c.sendOK(now) {
    45  			return time.Time{}
    46  		}
    47  		// We may still send ACKs, even if congestion control or pacing limit sending.
    48  
    49  		// Prepare to write a datagram of at most maxSendSize bytes.
    50  		c.w.reset(c.loss.maxSendSize())
    51  
    52  		dstConnID, ok := c.connIDState.dstConnID()
    53  		if !ok {
    54  			// It is currently not possible for us to end up without a connection ID,
    55  			// but handle the case anyway.
    56  			return time.Time{}
    57  		}
    58  
    59  		// Initial packet.
    60  		pad := false
    61  		var sentInitial *sentPacket
    62  		if c.keysInitial.canWrite() {
    63  			pnumMaxAcked := c.loss.spaces[initialSpace].maxAcked
    64  			pnum := c.loss.nextNumber(initialSpace)
    65  			p := longPacket{
    66  				ptype:     packetTypeInitial,
    67  				version:   quicVersion1,
    68  				num:       pnum,
    69  				dstConnID: dstConnID,
    70  				srcConnID: c.connIDState.srcConnID(),
    71  				extra:     c.retryToken,
    72  			}
    73  			c.w.startProtectedLongHeaderPacket(pnumMaxAcked, p)
    74  			c.appendFrames(now, initialSpace, pnum, limit)
    75  			if logPackets {
    76  				logSentPacket(c, packetTypeInitial, pnum, p.srcConnID, p.dstConnID, c.w.payload())
    77  			}
    78  			if c.logEnabled(QLogLevelPacket) && len(c.w.payload()) > 0 {
    79  				c.logPacketSent(packetTypeInitial, pnum, p.srcConnID, p.dstConnID, c.w.packetLen(), c.w.payload())
    80  			}
    81  			sentInitial = c.w.finishProtectedLongHeaderPacket(pnumMaxAcked, c.keysInitial.w, p)
    82  			if sentInitial != nil {
    83  				c.idleHandlePacketSent(now, sentInitial)
    84  				// Client initial packets and ack-eliciting server initial packaets
    85  				// need to be sent in a datagram padded to at least 1200 bytes.
    86  				// We can't add the padding yet, however, since we may want to
    87  				// coalesce additional packets with this one.
    88  				if c.side == clientSide || sentInitial.ackEliciting {
    89  					pad = true
    90  				}
    91  			}
    92  		}
    93  
    94  		// Handshake packet.
    95  		if c.keysHandshake.canWrite() {
    96  			pnumMaxAcked := c.loss.spaces[handshakeSpace].maxAcked
    97  			pnum := c.loss.nextNumber(handshakeSpace)
    98  			p := longPacket{
    99  				ptype:     packetTypeHandshake,
   100  				version:   quicVersion1,
   101  				num:       pnum,
   102  				dstConnID: dstConnID,
   103  				srcConnID: c.connIDState.srcConnID(),
   104  			}
   105  			c.w.startProtectedLongHeaderPacket(pnumMaxAcked, p)
   106  			c.appendFrames(now, handshakeSpace, pnum, limit)
   107  			if logPackets {
   108  				logSentPacket(c, packetTypeHandshake, pnum, p.srcConnID, p.dstConnID, c.w.payload())
   109  			}
   110  			if c.logEnabled(QLogLevelPacket) && len(c.w.payload()) > 0 {
   111  				c.logPacketSent(packetTypeHandshake, pnum, p.srcConnID, p.dstConnID, c.w.packetLen(), c.w.payload())
   112  			}
   113  			if sent := c.w.finishProtectedLongHeaderPacket(pnumMaxAcked, c.keysHandshake.w, p); sent != nil {
   114  				c.idleHandlePacketSent(now, sent)
   115  				c.loss.packetSent(now, handshakeSpace, sent)
   116  				if c.side == clientSide {
   117  					// "[...] a client MUST discard Initial keys when it first
   118  					// sends a Handshake packet [...]"
   119  					// https://www.rfc-editor.org/rfc/rfc9001.html#section-4.9.1-2
   120  					c.discardKeys(now, initialSpace)
   121  				}
   122  			}
   123  		}
   124  
   125  		// 1-RTT packet.
   126  		if c.keysAppData.canWrite() {
   127  			pnumMaxAcked := c.loss.spaces[appDataSpace].maxAcked
   128  			pnum := c.loss.nextNumber(appDataSpace)
   129  			c.w.start1RTTPacket(pnum, pnumMaxAcked, dstConnID)
   130  			c.appendFrames(now, appDataSpace, pnum, limit)
   131  			if pad && len(c.w.payload()) > 0 {
   132  				// 1-RTT packets have no length field and extend to the end
   133  				// of the datagram, so if we're sending a datagram that needs
   134  				// padding we need to add it inside the 1-RTT packet.
   135  				c.w.appendPaddingTo(paddedInitialDatagramSize)
   136  				pad = false
   137  			}
   138  			if logPackets {
   139  				logSentPacket(c, packetType1RTT, pnum, nil, dstConnID, c.w.payload())
   140  			}
   141  			if c.logEnabled(QLogLevelPacket) && len(c.w.payload()) > 0 {
   142  				c.logPacketSent(packetType1RTT, pnum, nil, dstConnID, c.w.packetLen(), c.w.payload())
   143  			}
   144  			if sent := c.w.finish1RTTPacket(pnum, pnumMaxAcked, dstConnID, &c.keysAppData); sent != nil {
   145  				c.idleHandlePacketSent(now, sent)
   146  				c.loss.packetSent(now, appDataSpace, sent)
   147  			}
   148  		}
   149  
   150  		buf := c.w.datagram()
   151  		if len(buf) == 0 {
   152  			if limit == ccOK {
   153  				// We have nothing to send, and congestion control does not
   154  				// block sending. The congestion window is underutilized.
   155  				c.loss.cc.setUnderutilized(true)
   156  			}
   157  			return next
   158  		}
   159  
   160  		if sentInitial != nil {
   161  			if pad {
   162  				// Pad out the datagram with zeros, coalescing the Initial
   163  				// packet with invalid packets that will be ignored by the peer.
   164  				// https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1-1
   165  				for len(buf) < paddedInitialDatagramSize {
   166  					buf = append(buf, 0)
   167  					// Technically this padding isn't in any packet, but
   168  					// account it to the Initial packet in this datagram
   169  					// for purposes of flow control and loss recovery.
   170  					sentInitial.size++
   171  					sentInitial.inFlight = true
   172  				}
   173  			}
   174  			// If we're a client and this Initial packet is coalesced
   175  			// with a Handshake packet, then we've discarded Initial keys
   176  			// since constructing the packet and shouldn't record it as in-flight.
   177  			if c.keysInitial.canWrite() {
   178  				c.loss.packetSent(now, initialSpace, sentInitial)
   179  			}
   180  		}
   181  
   182  		c.endpoint.sendDatagram(buf, c.peerAddr)
   183  	}
   184  }
   185  
   186  func (c *Conn) appendFrames(now time.Time, space numberSpace, pnum packetNumber, limit ccLimit) {
   187  	if c.lifetime.localErr != nil {
   188  		c.appendConnectionCloseFrame(now, space, c.lifetime.localErr)
   189  		return
   190  	}
   191  
   192  	shouldSendAck := c.acks[space].shouldSendAck(now)
   193  	if limit != ccOK {
   194  		// ACKs are not limited by congestion control.
   195  		if shouldSendAck && c.appendAckFrame(now, space) {
   196  			c.acks[space].sentAck()
   197  		}
   198  		return
   199  	}
   200  	// We want to send an ACK frame if the ack controller wants to send a frame now,
   201  	// OR if we are sending a packet anyway and have ack-eliciting packets which we
   202  	// have not yet acked.
   203  	//
   204  	// We speculatively add ACK frames here, to put them at the front of the packet
   205  	// to avoid truncation.
   206  	//
   207  	// After adding all frames, if we don't need to send an ACK frame and have not
   208  	// added any other frames, we abandon the packet.
   209  	if c.appendAckFrame(now, space) {
   210  		defer func() {
   211  			// All frames other than ACK and PADDING are ack-eliciting,
   212  			// so if the packet is ack-eliciting we've added additional
   213  			// frames to it.
   214  			if !shouldSendAck && !c.w.sent.ackEliciting {
   215  				// There's nothing in this packet but ACK frames, and
   216  				// we don't want to send an ACK-only packet at this time.
   217  				// Abandoning the packet means we wrote an ACK frame for
   218  				// nothing, but constructing the frame is cheap.
   219  				c.w.abandonPacket()
   220  				return
   221  			}
   222  			// Either we are willing to send an ACK-only packet,
   223  			// or we've added additional frames.
   224  			c.acks[space].sentAck()
   225  			if !c.w.sent.ackEliciting && c.shouldMakePacketAckEliciting() {
   226  				c.w.appendPingFrame()
   227  			}
   228  		}()
   229  	}
   230  	if limit != ccOK {
   231  		return
   232  	}
   233  	pto := c.loss.ptoExpired
   234  
   235  	// TODO: Add all the other frames we can send.
   236  
   237  	// CRYPTO
   238  	c.crypto[space].dataToSend(pto, func(off, size int64) int64 {
   239  		b, _ := c.w.appendCryptoFrame(off, int(size))
   240  		c.crypto[space].sendData(off, b)
   241  		return int64(len(b))
   242  	})
   243  
   244  	// Test-only PING frames.
   245  	if space == c.testSendPingSpace && c.testSendPing.shouldSendPTO(pto) {
   246  		if !c.w.appendPingFrame() {
   247  			return
   248  		}
   249  		c.testSendPing.setSent(pnum)
   250  	}
   251  
   252  	if space == appDataSpace {
   253  		// HANDSHAKE_DONE
   254  		if c.handshakeConfirmed.shouldSendPTO(pto) {
   255  			if !c.w.appendHandshakeDoneFrame() {
   256  				return
   257  			}
   258  			c.handshakeConfirmed.setSent(pnum)
   259  		}
   260  
   261  		// NEW_CONNECTION_ID, RETIRE_CONNECTION_ID
   262  		if !c.connIDState.appendFrames(c, pnum, pto) {
   263  			return
   264  		}
   265  
   266  		// All stream-related frames. This should come last in the packet,
   267  		// so large amounts of STREAM data don't crowd out other frames
   268  		// we may need to send.
   269  		if !c.appendStreamFrames(&c.w, pnum, pto) {
   270  			return
   271  		}
   272  
   273  		if !c.appendKeepAlive(now) {
   274  			return
   275  		}
   276  	}
   277  
   278  	// If this is a PTO probe and we haven't added an ack-eliciting frame yet,
   279  	// add a PING to make this an ack-eliciting probe.
   280  	//
   281  	// Technically, there are separate PTO timers for each number space.
   282  	// When a PTO timer expires, we MUST send an ack-eliciting packet in the
   283  	// timer's space. We SHOULD send ack-eliciting packets in every other space
   284  	// with in-flight data. (RFC 9002, section 6.2.4)
   285  	//
   286  	// What we actually do is send a single datagram containing an ack-eliciting packet
   287  	// for every space for which we have keys.
   288  	//
   289  	// We fill the PTO probe packets with new or unacknowledged data. For example,
   290  	// a PTO probe sent for the Initial space will generally retransmit previously
   291  	// sent but unacknowledged CRYPTO data.
   292  	//
   293  	// When sending a PTO probe datagram containing multiple packets, it is
   294  	// possible that an earlier packet will fill up the datagram, leaving no
   295  	// space for the remaining probe packet(s). This is not a problem in practice.
   296  	//
   297  	// A client discards Initial keys when it first sends a Handshake packet
   298  	// (RFC 9001 Section 4.9.1). Handshake keys are discarded when the handshake
   299  	// is confirmed (RFC 9001 Section  4.9.2). The PTO timer is not set for the
   300  	// Application Data packet number space until the handshake is confirmed
   301  	// (RFC 9002 Section 6.2.1). Therefore, the only times a PTO probe can fire
   302  	// while data for multiple spaces is in flight are:
   303  	//
   304  	// - a server's Initial or Handshake timers can fire while Initial and Handshake
   305  	//   data is in flight; and
   306  	//
   307  	// - a client's Handshake timer can fire while Handshake and Application Data
   308  	//   data is in flight.
   309  	//
   310  	// It is theoretically possible for a server's Initial CRYPTO data to overflow
   311  	// the maximum datagram size, but unlikely in practice; this space contains
   312  	// only the ServerHello TLS message, which is small. It's also unlikely that
   313  	// the Handshake PTO probe will fire while Initial data is in flight (this
   314  	// requires not just that the Initial CRYPTO data completely fill a datagram,
   315  	// but a quite specific arrangement of lost and retransmitted packets.)
   316  	// We don't bother worrying about this case here, since the worst case is
   317  	// that we send a PTO probe for the in-flight Initial data and drop the
   318  	// Handshake probe.
   319  	//
   320  	// If a client's Handshake PTO timer fires while Application Data data is in
   321  	// flight, it is possible that the resent Handshake CRYPTO data will crowd
   322  	// out the probe for the Application Data space. However, since this probe is
   323  	// optional (recall that the Application Data PTO timer is never set until
   324  	// after Handshake keys have been discarded), dropping it is acceptable.
   325  	if pto && !c.w.sent.ackEliciting {
   326  		c.w.appendPingFrame()
   327  	}
   328  }
   329  
   330  // shouldMakePacketAckEliciting is called when sending a packet containing nothing but an ACK frame.
   331  // It reports whether we should add a PING frame to the packet to make it ack-eliciting.
   332  func (c *Conn) shouldMakePacketAckEliciting() bool {
   333  	if c.keysAppData.needAckEliciting() {
   334  		// The peer has initiated a key update.
   335  		// We haven't sent them any packets yet in the new phase.
   336  		// Make this an ack-eliciting packet.
   337  		// Their ack of this packet will complete the key update.
   338  		return true
   339  	}
   340  	if c.loss.consecutiveNonAckElicitingPackets >= 19 {
   341  		// We've sent a run of non-ack-eliciting packets.
   342  		// Add in an ack-eliciting one every once in a while so the peer
   343  		// lets us know which ones have arrived.
   344  		//
   345  		// Google QUICHE injects a PING after sending 19 packets. We do the same.
   346  		//
   347  		// https://www.rfc-editor.org/rfc/rfc9000#section-13.2.4-2
   348  		return true
   349  	}
   350  	// TODO: Consider making every packet sent when in PTO ack-eliciting to speed up recovery.
   351  	return false
   352  }
   353  
   354  func (c *Conn) appendAckFrame(now time.Time, space numberSpace) bool {
   355  	seen, delay := c.acks[space].acksToSend(now)
   356  	if len(seen) == 0 {
   357  		return false
   358  	}
   359  	d := unscaledAckDelayFromDuration(delay, ackDelayExponent)
   360  	return c.w.appendAckFrame(seen, d)
   361  }
   362  
   363  func (c *Conn) appendConnectionCloseFrame(now time.Time, space numberSpace, err error) {
   364  	c.sentConnectionClose(now)
   365  	switch e := err.(type) {
   366  	case localTransportError:
   367  		c.w.appendConnectionCloseTransportFrame(e.code, 0, e.reason)
   368  	case *ApplicationError:
   369  		if space != appDataSpace {
   370  			// "CONNECTION_CLOSE frames signaling application errors (type 0x1d)
   371  			// MUST only appear in the application data packet number space."
   372  			// https://www.rfc-editor.org/rfc/rfc9000#section-12.5-2.2
   373  			c.w.appendConnectionCloseTransportFrame(errApplicationError, 0, "")
   374  		} else {
   375  			c.w.appendConnectionCloseApplicationFrame(e.Code, e.Reason)
   376  		}
   377  	default:
   378  		// TLS alerts are sent using error codes [0x0100,0x01ff).
   379  		// https://www.rfc-editor.org/rfc/rfc9000#section-20.1-2.36.1
   380  		var alert tls.AlertError
   381  		switch {
   382  		case errors.As(err, &alert):
   383  			// tls.AlertError is a uint8, so this can't exceed 0x01ff.
   384  			code := errTLSBase + transportError(alert)
   385  			c.w.appendConnectionCloseTransportFrame(code, 0, "")
   386  		default:
   387  			c.w.appendConnectionCloseTransportFrame(errInternal, 0, "")
   388  		}
   389  	}
   390  }
   391  

View as plain text