RFC 4342

Profile for Datagram Congestion Control Protocol (DCCP) Congestion Control ID 3: TCP-Friendly Rate Control (TFRC)

๐Ÿ“… published April 2006, from ietf

RFC 4342 ๐ŸŽ€ is a Standards Track doc from the Network Working Group, dated March 2006. The authors are S. Floyd (ICIR), E. Kohler (UCLA), and J. Padhye (Microsoft Research). Its title is Profile for Datagram Congestion Control Protocol (DCCP) Congestion Control ID 3: TCP-Friendly Rate Control (TFRC). The title block doesn't list anything it obsoletes or updates ๐Ÿ’…


Status of This Memo โœจ

okay so this doc is an Internet standards track protocol for the Internet community, and it wants discussion and suggestions for improvements ๐Ÿซถ Check the current edition of "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited.

Copyright (C) The Internet Society (2006).

Abstract ๐Ÿฆ‹

this doc is the profile for Congestion Control Identifier 3, TCP-Friendly Rate Control (TFRC), in the Datagram Congestion Control Protocol (DCCP). CCID 3 should be used by senders that want a TCP-friendly sending rate, possibly with Explicit Congestion Notification (ECN), while minimizing abrupt rate changes ๐Ÿ’œ

Table of Contents ๐Ÿงš

   1. Introduction ....................................................2
   2. Conventions .....................................................3
   3. Usage ...........................................................3
      3.1. Relationship with TFRC .....................................4
      3.2. Half-Connection Example ....................................4
   4. Connection Establishment ........................................5
   5. Congestion Control on Data Packets ..............................5
      5.1. Response to Idle and Application-Limited Periods ...........7
      5.2. Response to Data Dropped and Slow Receiver .................8
      5.3. Packet Sizes ...............................................9
   6. Acknowledgements ................................................9
      6.1. Loss Interval Definition ..................................10
           6.1.1. Loss Interval Lengths ..............................12
      6.2. Congestion Control on Acknowledgements ....................13
      6.3. Acknowledgements of Acknowledgements ......................13
      6.4. Determining Quiescence ....................................14
   7. Explicit Congestion Notification ...............................14
   8. Options and Features ...........................................14
      8.1. Window Counter Value ......................................15
      8.2. Elapsed Time Options ......................................17
      8.3. Receive Rate Option .......................................17
      8.4. Send Loss Event Rate Feature ..............................18
      8.5. Loss Event Rate Option ....................................18
      8.6. Loss Intervals Option .....................................18
           8.6.1. Option Details .....................................19
           8.6.2. Example ............................................20
   9. Verifying Congestion Control Compliance with ECN ...............22
      9.1. Verifying the ECN Nonce Echo ..............................22
      9.2. Verifying the Reported Loss Intervals and Loss
           Event Rate ................................................23
   10. Implementation Issues .........................................23
      10.1. Timestamp Usage ..........................................23
      10.2. Determining Loss Events at the Receiver ..................24
      10.3. Sending Feedback Packets .................................25
   11. Security Considerations .......................................27
   12. IANA Considerations ...........................................28
      12.1. Reset Codes ..............................................28
      12.2. Option Types .............................................28
      12.3. Feature Numbers ..........................................28
   13. Thanks ........................................................29
   A. Appendix: Possible Future Changes to CCID 3 ....................30
   Normative References ..............................................31
   Informative References ............................................31

List of Tables

   Table 1: DCCP CCID 3 Options ......................................14
   Table 2: DCCP CCID 3 Feature Numbers ..............................15

1. Introduction โœจ

this doc is the profile for Congestion Control Identifier 3, TCP-Friendly Rate Control (TFRC), in the Datagram Congestion Control Protocol (DCCP) [RFC4340]. DCCP uses Congestion Control Identifiers, or CCIDs, to specify which congestion control mechanism is in use on a half-connection ๐Ÿ’–

TFRC is a receiver-based congestion control mechanism. It gives you a TCP-friendly sending rate while keeping the abrupt rate changes that TCP, or TCP-like congestion control, is known for way lower [RFC3448]. The sender's allowed sending rate is set in response to the loss event rate, which the receiver typically reports back to the sender. See Section 3 for more on application requirements ๐Ÿฆ‹

2. Conventions ๐ŸŽ€

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].

all multi-byte numerical quantities in CCID 3, like arguments to options, are sent in network byte order (most significant byte first) ๐Ÿ”ข

a DCCP half-connection is the application data sent by one endpoint plus the matching acknowledgements sent by the other endpoint. "HC-Sender" and "HC-Receiver" are the endpoints sending application data and acknowledgements, respectively. CCIDs apply at the level of half-connections, so we shorten HC-Sender to "sender" and HC-Receiver to "receiver" in this doc. See [RFC4340] for more discussion ๐Ÿ’…

for simplicity, senders send DCCP-Data packets and receivers send DCCP-Ack packets. Both of these categories are meant to include DCCP-DataAck packets ๐Ÿซถ

the phrases "ECN-marked" and "marked" refer to packets marked ECN Congestion Experienced, unless noted otherwise ๐ŸŒˆ

this doc uses a few variables from [RFC3448], including the following:

  • X_recv: the receive rate in bytes per second. See [RFC3448], Section 3.2.2.
  • s: the packet size in bytes. See [RFC3448], Section 3.1.
  • p: the loss event rate. See [RFC3448], Section 3.1.

3. Usage ๐Ÿ“

CCID 3's TFRC congestion control is a great fit for flows that want to minimize abrupt changes in the sending rate, like streaming media apps with small or moderate receiver buffering before playback ๐ŸŽฌ TCP-like congestion control, like DCCP's CCID 2 [RFC4341], cuts the sending rate in half on each congestion event, so it can't give you a relatively smooth sending rate. not it ๐Ÿ™…โ€โ™€๏ธ

as explained in [RFC3448], Section 1, the trade-off for getting smoother throughput than TCP while still competing fairly for bandwidth with TCP is that the TFRC mechanism in CCID 3 reacts more slowly to changes in available bandwidth than TCP or TCP-like mechanisms do. so CCID 3 should only be used for applications that actually need smooth throughput ๐Ÿ˜ค If an app just wants to move as much data as possible in as little time as possible, we recommend TCP-like congestion control, like CCID 2 ๐Ÿƒโ€โ™€๏ธ

CCID 3 should also not be used by applications that change their sending rate by varying the packet size, rather than by varying the rate at which packets are sent. A new CCID will be required for these applications ๐Ÿšซ

3.1. Relationship with TFRC ๐Ÿ’œ

the congestion control mechanisms described here follow the TFRC mechanism standardized by the IETF [RFC3448]. Conforming CCID 3 implementations MAY track updates to the TCP throughput equation directly, as those updates are standardized in the IETF, instead of waiting for revisions of this document. However, conforming implementations SHOULD wait for explicit updates to CCID 3 before implementing other changes to TFRC congestion control ๐Ÿง 

3.2. Half-Connection Example ๐ŸŒธ

this example shows the typical progress of a half-connection using CCID 3's TFRC congestion control. It doesn't include connection initiation and termination. The example is informative, not normative ๐Ÿ“–

  1. The sender transmits DCCP-Data packets. Its sending rate is governed by the allowed transmit rate, as specified in [RFC3448], Section 3.2. Each DCCP-Data packet has a sequence number, and the DCCP header's CCVal field contains the window counter value, which the receiver uses to decide when multiple losses belong in a single loss event ๐Ÿ”ฅ

In the typical case of an ECN-capable half-connection, each DCCP-Data and DCCP-DataAck packet is sent as ECN Capable, with either the ECT(0) or the ECT(1) codepoint set. The use of the ECN Nonce with TFRC is described in Section 9 ๐ŸŒˆ

  1. The receiver sends DCCP-Ack packets that acknowledge the data packets at least once per round-trip time, unless the sender is sending at a rate of less than one packet per round-trip time, as indicated by the TFRC specification ([RFC3448], Section 6) โญ Each DCCP-Ack packet uses a sequence number, identifies the most recent packet received from the sender, and includes feedback about the recent loss intervals the receiver has experienced ๐Ÿซง
  2. The sender keeps sending DCCP-Data packets, controlled by the allowed transmit rate. When it gets DCCP-Ack packets, the sender updates its allowed transmit rate as specified in [RFC3448], Section 4.3. That update is based on a loss event rate the sender calculates using the receiver's loss intervals feedback. If it prefers, the sender can also use a loss event rate that the receiver calculated and reported ๐Ÿ‘‘
  3. The sender estimates round-trip times and calculates a nofeedback time, as specified in [RFC3448], Section 4.4. If no feedback comes back from the receiver in that time (at least four round-trip times), the sender halves its sending rate ๐Ÿ˜ญ

4. Connection Establishment ๐Ÿงš

the client starts the connection using the mechanisms in the DCCP specification [RFC4340]. During or after CCID 3 negotiation, the client and/or server may want to negotiate the values of the Send Ack Vector and Send Loss Event Rate features ๐ŸŽ€

part 1 of 7 done bestie, more sections are coming โœจ

5. Congestion Control on Data Packets โœจ

okay bestie, CCID 3 borrows its whole congestion control brain from TFRC [RFC3448] ๐Ÿง ๐Ÿ’–. This section is basically a recap of [RFC3448]. That one is still the source to trust, and [RFC3448] should be considered normative except where specifically indicated otherwise ๐Ÿ’….

Loss Event Rate ๐ŸŒง๏ธ

So the whole CCID 3 vibe revolves around one number: the loss event rate ๐Ÿ“‰. That's the number of loss events as a fraction of packets sent, weighted over the last few loss intervals. You plug that, a round-trip time estimate, and the average packet size into the TCP throughput equation (see [RFC3448], Section 3.1) and boom, you get a fair transmit rate that's pretty close to what a modern TCP would get in the same conditions ๐Ÿ”ฅ. CCID 3 senders are limited to this fair rate ๐Ÿ™Œ.

The loss event rate is calculated from recent loss interval lengths that the receiver reports ๐Ÿงฎ. Loss intervals are defined super precisely in Section 6.1 ๐Ÿ”. The quick version: a loss interval is up to 1 RTT of possibly lost or ECN-marked data packets, followed by an arbitrary number of non-dropped, non-marked data packets. So long loss intervals mean low congestion rates ๐ŸŒฟ. The receiver reports those lengths with the CCID 3 Loss Intervals option, which you can read about in Section 8.6 ๐Ÿ“ฆ.

Other Congestion Control Mechanisms โš™๏ธ

The sender starts in a slow-start phase, roughly doubling its allowed sending rate each round-trip time ๐Ÿš€. Slow-start ends when the receiver reports a data packet drop or mark. After that, the sender uses the loss event rate to work out its allowed sending rate ๐ŸŽฏ.

[RFC3448], Section 4, sets the initial sending rate at one packet per RTT, like so: the sender starts with an allowed rate of one packet per second. Once a feedback packet arrives, it has an RTT measurement, and then it sets the initial allowed rate to one packet per RTT ๐Ÿ•ฐ๏ธ. But here's the plot twist: TCP's initial window used to be one segment, yet [RFC2581] allows two segments, and [RFC3390] allows three or four segments (up to 4380 bytes) โœจ. [RFC3390] gives an upper bound on the initial window of:

min(4*MSS, max(2*MSS, 4380 bytes))

So, unlike [RFC3448], the initial CCID 3 sending rate is allowed to be at least two packets per RTT, and at most four packets per RTT, depending on the packet size ๐ŸŽ€. The initial rate is only allowed to be three or four packets per RTT when, in terms of segment size, that translates to at most 4380 bytes per RTT ๐Ÿ’….

The sender measures RTT using the Elapsed Time and/or Timestamp Echo option in feedback packets, as described in Section 8.2 โฑ๏ธ. The Elapsed Time option is required, but Timestamp Echo is not. The sender keeps an average RTT that's heavily weighted toward the most recent measurements ๐Ÿ’œ.

Every DCCP-Data packet has a sequence number ๐Ÿ”ข. Each one also carries a window counter value (see Section 8.1). The window counter is generally incremented by one every quarter round-trip time ๐Ÿ•. The receiver uses it as a coarse-grained timestamp to figure out when a packet loss belongs to an existing loss interval and when it has to start a brand new one ๐Ÿงฉ.

Because TFRC is rate-based instead of window-based, and feedback packets can get dropped in the network, the sender needs a way to slow down when it hears nothing back ๐Ÿ˜ฌ. As described in Section 6, the receiver sends feedback packets roughly once per RTT. As specified in [RFC3448], Section 4.3, the sender sets a nofeedback timer to at least four round-trip times, or to twice the interval between data packets, whichever is larger โฒ๏ธ. If the timer expires and no feedback has arrived, the sender halves its allowed sending rate. The allowed sending rate is never reduced below one packet per 64 seconds ๐Ÿข.

Note that not all acknowledgements count as feedback packets, since feedback packets must contain valid Loss Intervals, Elapsed Time, and Receive Rate options ๐Ÿ™ˆ.

If the sender never receives a feedback packet, it never gets to set the allowed rate to one packet per RTT. So the sending rate stays at its initial rate of one packet per second, with the nofeedback timer expiring after two seconds โณ. The allowed rate gets halved every time that timer expires. So if no feedback ever shows up, the allowed rate is never above one packet per second and quickly drops below it ๐Ÿ“‰๐Ÿ’€.

The feedback packets from the receiver contain a Receive Rate option, which says how fast data packets arrived at the receiver since the last feedback packet ๐Ÿ“ฌ. The allowed sending rate can be at most twice the rate the receiver got in the last round-trip time ๐Ÿ™Œ. That might be less than the nominal fair rate if, for example, the app is sending less than its fair share ๐ŸŒฟ.

5.1. Response to Idle and Application-Limited Periods


One consequence of the nofeedback timer is that the sender lowers its allowed sending rate after being idle for a significant stretch of time ๐Ÿ˜ด. In [RFC3448], Section 4.4, the allowed sending rate is never reduced to fewer than two packets per round-trip time because of an idle period. CCID 3 changes this to account for the larger initial windows allowed by [RFC3390]. Now the allowed sending rate is never reduced to less than the [RFC3390] initial sending rate because of an idle period ๐Ÿซถ.

If the allowed sending rate is less than the initial sending rate when the idle period starts, it will still be less than the initial sending rate when the idle period ends. But if the allowed sending rate is greater than or equal to the initial sending rate when the idle period starts, then it should not be reduced below the initial sending rate, no matter how long the idle period lasts ๐Ÿ’–.

The sender's allowed sending rate is limited to at most twice the receive rate reported by the receiver. So after an application-limited period, the sender can at most double its sending rate from one round-trip time to the next, until it reaches the allowed sending rate determined by the loss event rate ๐Ÿš€โœจ.

5.2. Response to Data Dropped and Slow Receiver


DCCP's Data Dropped option lets a receiver say that a packet got dropped at the end host before it reached the app, for instance because of corruption or receive buffer overflow ๐Ÿ’ง. Its Slow Receiver option lets a receiver say it's struggling to keep up with the sender's packets, even though nothing has been dropped yet ๐ŸŒ. CCID 3 senders respond to these options as described in [RFC4340], with the following further clarifications ๐Ÿ“.

  • Drop Code 2 ("receive buffer drop"). The allowed sending rate is reduced by one packet per RTT for each packet newly acknowledged as Drop Code 2, except that it is never reduced below one packet per RTT as a result of Drop Code 2.
  • Adjusting the receive rate X_recv. A CCID 3 sender SHOULD also respond to non-network-congestion events, such as those implied by Data Dropped and Slow Receiver options, by adjusting X_recv, the receive rate reported by the receiver in Receive Rate options (see Section 8.3). The CCID 3 sender's allowed sending rate is limited to at most twice the receive rate reported by the receiver via the min(..., 2*X_recv) clause in TFRC's throughput calculations ([RFC3448], Section 4.3). When the sender receives one or more Data Dropped and Slow Receiver options, the sender adjusts X_recv as follows:
    1. X_inrecv is equal to the Receive Rate in bytes per second reported by the receiver in the most recent acknowledgement.
    2. X_drop is set to the sending rate upper bound implied by Data Dropped and Slow Receiver options. If the sender receives a Slow Receiver option, which requests that the sender not increase its sending rate for roughly a round-trip time [RFC4340], then X_drop should be set to X_inrecv. Similarly, if the sender receives a Data Dropped option indicating, for example, that three packets were dropped with Drop Code 2, then the upper bound on the sending rate will be decreased by at most three packets per RTT, by the sender setting X_drop to:
max(X_inrecv - 3*s/RTT, min(X_inrecv, s/RTT))

Again, s is the packet size in bytes.

  1. X_recv is then set to min(X_inrecv, X_drop/2).

As a result, the next round-trip time's sending rate will be limited to at most 2*(X_drop/2) = X_drop. The effects of the Slow Receiver and Data Dropped options on X_recv will mostly vanish by the round-trip time after that, which is appropriate for this non-network-congestion feedback. This procedure MUST only be used for those Drop Codes not related to corruption (see [RFC4340]). Currently, this is limited to Drop Codes 0, 1, and 2 ๐Ÿงฏ.

5.3. Packet Sizes


CCID 3 is intended for applications that use a fixed packet size and that vary their sending rate in packets per second in response to congestion ๐Ÿ“ฆ. CCID 3 is not appropriate for applications that need a fixed interval of time between packets and vary their packet size instead of their packet rate in response to congestion ๐Ÿ™…โ€โ™€๏ธ. However, some attention might be required for applications using CCID 3 that vary their packet size not in response to congestion, but in response to other application-level requirements ๐Ÿ‘€.

The packet size s is used in the TCP throughput equation. A CCID 3 implementation MAY calculate s as the segment size averaged over multiple round trip times, for example over the most recent four loss intervals, for loss intervals as defined in Section 6.1 ๐Ÿ“Š. Alternately, a CCID 3 implementation MAY use the Maximum Packet Size to derive s. In this case, s is set to the Maximum Segment Size (MSS), the maximum size in bytes for the data segment, not including the default DCCP and IP packet headers ๐Ÿ“. Each packet transmitted then counts as one MSS, regardless of the actual segment size, and the TCP throughput equation can be interpreted as specifying the sending rate in packets per second ๐Ÿง.

CCID 3 implementations MAY check for applications that appear to be manipulating the packet size inappropriately ๐Ÿ•ต๏ธ. For example, an application might send small packets for a while, building up a fast rate, then switch to large packets to take advantage of the fast rate ๐Ÿƒโ€โ™€๏ธ. (Preliminary simulations indicate that applications may not be able to increase their overall transfer rates this way, so it is not clear that this manipulation will occur in practice [V03].) ๐Ÿคทโ€โ™€๏ธ

6. Acknowledgements โœจ

okay so the receiver sends a feedback packet to the sender roughly once per round-trip time ๐ŸŒธ, but only if the sender is sending packets that frequently. the rate is set by the TFRC protocol, see [RFC3448], Section 6 ๐Ÿซถ.

every feedback packet carries an Acknowledgement Number, which equals the greatest valid sequence number received so far on this connection. ("Greatest" is measured in circular sequence space, obviously ๐Ÿ™„) each feedback packet also has at least these options:

  1. An Elapsed Time and/or Timestamp Echo option, which says how long it's been since the packet whose sequence number is in the Acknowledgement Number field arrived at the receiver. these options are described in [RFC4340], Section 13.
  2. A Receive Rate option, defined in Section 8.3, which says what rate data was received at since the last DCCP-Ack was sent.
  3. A Loss Intervals option, defined in Section 8.6, which shows the most recent loss intervals the receiver has experienced. (we'll get to what a loss interval is in a sec ๐Ÿ‘€) from Loss Intervals, the sender can easily work out the loss event rate p using the procedure in [RFC3448], Section 5.4.

acknowledgements that don't have at least these three options are not considered feedback packets ๐Ÿšซ no vibes, no feedback.

the receiver MAY also include other options about the loss event rate ๐Ÿ’…. that includes Loss Event Rate, which gives the loss event rate the receiver calculated itself (Section 8.5), and DCCP's generic Ack Vector option, which reports the exact sequence numbers of any lost or marked packets ([RFC4340], Section 11.4). Ack Vector isn't required by CCID 3's congestion control. the Loss Intervals option already has everything needed to manage the transmit rate and probabilistically verify receiver feedback โœจ but Ack Vector can be super useful for apps that need to know exactly which packets got lost. the receiver MAY also include other acknowledgement-related options, like DCCP's Data Dropped option ([RFC4340], Section 11.7) ๐ŸŽ€.

and if the HC-Receiver is also sending data packets back to the HC-Sender, it MAY piggyback acknowledgement info on those data packets more often than TFRC's acknowledgement rate allows ๐Ÿฆ‹

6.1. Loss Interval Definition

as described in [RFC3448], Section 5.2, a loss interval starts with a lost or ECN-marked data packet ๐Ÿ’€; then comes at most one round-trip time's worth of packets that may or may not be lost or marked; and it ends with a super long series of non-dropped, non-marked data packets ๐Ÿงš. for example, here's a single loss interval, with sequence numbers going up as you move right:

        Lossy Part
         <= 1 RTT   __________ Lossless Part __________
       /          \/                                   \
       *----*--*--*-------------------------------------
       ^    ^  ^  ^
      losses or marks

btw a loss interval's lossless part can be empty ๐Ÿ™ˆ like the first interval below:

       Lossy Part   Lossy Part
        <= 1 RTT     <= 1 RTT   _____ Lossless Part _____
      /          \/           \/                         \
      *----*--*--***--------*-*---------------------------
      ^    ^  ^  ^^^        ^ ^
      \_ Int. 1 _/\_____________ Interval 2 _____________/

the lossy part's length MUST be less than or equal to 1 RTT, same as in [RFC3448], Section 5.2 ๐Ÿ”ฅ. CCID 3 uses window counter values, not receive times, to figure out whether multiple packets happened in the same RTT and so belong to the same loss event. see Section 10.2 for that. a loss interval whose lossy part lasts more than 1 RTT, or whose lossless part has a dropped or marked data packet, is invalid โŒ not it.

a missing data packet doesn't start a new loss interval until NDUPACK packets have been seen after the "hole", where NDUPACK = 3 ๐Ÿ‘€. so up to NDUPACK of the most recent sequence numbers (including the sequence numbers of any holes) might not be part of any loss interval for a bit, while the implementation waits to see whether the hole gets filled. for more on NDUPACK, see [RFC3448], Section 5.1, and [RFC2581], Section 3.2.

as [RFC3448], Section 5 says, every loss interval except the first starts with a lost or marked data packet, and every loss interval is as long as possible, as long as it still follows the validity rules above ๐Ÿ“

lost and ECN-marked non-data packets can happen freely in the lossless part of a loss interval ๐ŸŒˆ. (non-data packets are the ones that can't carry application data: DCCP-Ack, DCCP-Close, DCCP-CloseReq, DCCP-Reset, DCCP-Sync, and DCCP-SyncAck.) if there's no better info, a receiver MUST conservatively assume that every lost packet was a data packet, so it must sit in some lossy part ๐Ÿซ . DCCP's NDP Count option can help the receiver figure out whether a particular packet had data in it, see [RFC4340], Section 7.7.

6.1.1. Loss Interval Lengths

[RFC3448] defines the TFRC congestion control mechanism for a one-way transfer of data, with data packets going from sender to receiver and feedback packets going back from receiver to sender. but CCID 3 lives in a world of two half-connections, where DCCP-Data and DCCP-DataAck packets from one half-connection share sequence number space with DCCP-Ack packets from the other ๐Ÿ”„. for CCID 3 congestion control, loss interval lengths should include data packets and should exclude the acks from the reverse half-connection. it's still useful to report the total number of packets in each loss interval though (for example, to help with ECN Nonce verification) ๐Ÿง .

so CCID 3's Loss Intervals option reports three lengths for each loss interval: the lengths of the lossy and lossless parts we talked about above, plus a separate data length ๐ŸŽ€. first, the lossy and lossless lengths are measured in sequence numbers. together they add up to the interval's sequence length, which is the total number of packets the sender transmitted during the interval. in DCCP this is easy to calculate: it's the greatest packet sequence number in the interval minus the greatest packet sequence number in the preceding interval (or, if there's no preceding interval, the predecessor of the half-connection's initial sequence number) ๐Ÿงฎ. the interval's data length is the number used in TFRC's loss event rate calculation, as defined in [RFC3448], Section 5, and it's calculated like this.

for all loss intervals except the first, the data length equals the sequence length minus the number of non-data packets the sender transmitted during the loss interval, except the minimum data length is one packet ๐Ÿ’–. if there's no better info, an endpoint MUST conservatively assume the loss interval only had data packets, in which case the data length equals the sequence length ๐Ÿ™. for more precision, the sender can work out the exact number of non-data packets in an interval by remembering which sent packets had data. the receiver can account for received non-data packets by leaving them out of the data length, and for packets that never showed up, it may be able to tell lost data packets apart from lost non-data packets using DCCP's NDP Count option ๐Ÿ•ต๏ธโ€โ™€๏ธ.

the first loss interval's data length is undefined until the first loss event ๐Ÿซฅ. [RFC3448], Section 6.3.1 says how the first loss interval's data length gets calculated once the first loss event happens, and this calculation uses X_recv, the most recent receive rate, as input. until that first loss event, the loss event rate is zero, and so is the data length reported for the interval in the Loss Intervals option 0๏ธโƒฃ.

the first loss interval's data length might be less than, equal to, or even greater than its sequence length ๐Ÿคฏ. any other loss interval's data length must be less than or equal to its sequence length ๐Ÿ“.

a sender MAY use the loss event rate or loss interval data lengths the receiver reports, or it MAY recalculate the loss event rate and/or loss interval data lengths using receiver feedback and any extra info it has ๐Ÿง . for example, say the network drops a DCCP-Ack packet with sequence number 50. the receiver might then report a loss interval starting at sequence number 50. if the sender figures out that this loss interval actually had no lost or ECN-marked data packets, it might merge the loss interval with the previous one, which gives a bigger allowed transmit rate ๐Ÿš€.

6.2. Congestion Control on Acknowledgements

the rate and timing for generating acknowledgements is set by the TFRC algorithm ([RFC3448], Section 6). the sending rate for acks is pretty low (roughly once per round-trip time), so there's no need for explicit congestion control on acknowledgements ๐Ÿ˜Œโœจ.

6.3. Acknowledgements of Acknowledgements

TFRC acknowledgements don't usually need to be reliable, so the sender generally doesn't need to acknowledge the receiver's acknowledgements ๐Ÿ™ƒ. but when Ack Vector or Data Dropped is used, the sender, DCCP A, MUST occasionally acknowledge the receiver's acknowledgements so the receiver can free up its Ack Vector or Data Dropped state ๐Ÿงน. when both half-connections are active, the needed acks will ride along in A's acknowledgements to B's data ๐Ÿ’ซ. but if the B-to-A half-connection goes quiet, DCCP A must send an acknowledgement proactively ๐Ÿ“ฃ.

so, when Ack Vector or Data Dropped is used, an active sender MUST acknowledge the receiver's acknowledgements about once per round-trip time, within a factor of two or three, probably by sending a DCCP-DataAck packet ๐Ÿ’…. no acknowledgement options are needed, just the Acknowledgement Number in the DCCP-DataAck header.

the sender MAY choose to acknowledge the receiver's acknowledgements even if they don't contain Ack Vectors or Data Dropped options ๐ŸŒธ. for instance, regular acknowledgements can shrink the size of the Loss Intervals option. but unlike Ack Vector and Data Dropped, the Loss Intervals option is bounded in size (and receiver state), so acks-of-acks aren't required ๐Ÿ™Œ.

6.4. Determining Quiescence

this section explains how a CCID 3 receiver figures out that the matching sender isn't sending any data and so has gone quiescent ๐Ÿ˜ด. see [RFC4340], Section 11.1, for general info on quiescence.

let T equal the greater of 0.2 seconds and two round-trip times ๐ŸŽ€. (a CCID 3 receiver has a rough measure of the round-trip time so it can pace its acknowledgements โฑ๏ธ.) the receiver detects that the sender has gone quiescent after T seconds pass without any additional data from the sender ๐Ÿซถ.


7. Explicit Congestion Notification ๐Ÿฆ‹

Okay so CCID 3 supports Explicit Congestion Notification (ECN) [RFC3168] ๐Ÿ’…. In the typical case of an ECN-capable half-connection (where the receiver's ECN Incapable feature is set to zero), the sender will use the ECN Nonce for its data packets, as specified in [RFC4340], Section 12.2. Information about the ECN Nonce MUST be returned by the receiver using the Loss Intervals option, and any Ack Vector options MUST include the ECN Nonce Sum ๐Ÿ”ฅ. The sender MAY maintain a table with the ECN nonce sum for each packet and use this information to probabilistically verify the ECN nonce sums returned in Loss Intervals or Ack Vector options. Section 9 describes this further โœจ.

8. Options and Features ๐ŸŽ€

CCID 3 can make use of DCCP's Ack Vector, Timestamp, Timestamp Echo, and Elapsed Time options, and its Send Ack Vector and ECN Incapable features ๐Ÿงš. In addition, the following CCID-specific options are defined for use with CCID 3.

                 Option                        DCCP-   Section
        Type     Length     Meaning            Data?  Reference
        -----    ------     -------            -----  ---------
       128-191              Reserved
         192        6       Loss Event Rate      N      8.5
         193     variable   Loss Intervals       N      8.6
         194        6       Receive Rate         N      8.3
       195-255              Reserved

                     Table 1: DCCP CCID 3 Options

The "DCCP-Data?" column indicates that all currently defined CCID 3-specific options MUST be ignored when they occur on DCCP-Data packets ๐Ÿ™ˆ.

The following CCID-specific feature is also defined.

                                      Rec'n Initial        Section
    Number   Meaning                  Rule   Value  Req'd Reference
    ------   -------                  -----  -----  ----- ---------
    128-191  Reserved
      192    Send Loss Event Rate      SP      0      N      8.4
    193-255  Reserved

                 Table 2: DCCP CCID 3 Feature Numbers

The column meanings are described in [RFC4340], Table 4. "Rec'n Rule" defines the feature's reconciliation rule, where "SP" means server-priority. "Req'd" specifies whether every CCID 3 implementation MUST understand a feature; Send Loss Event Rate is optional, in that it behaves like an extension ([RFC4340], Section 15) ๐ŸŒธ.

8.1. Window Counter Value

The data sender stores a 4-bit window counter value in the DCCP generic header's CCVal field on every data packet it sends. This value is set to 0 at the beginning of the transmission and generally increased by 1 every quarter of a round-trip time, as described in [RFC3448], Section 3.2.1. Window counters use circular arithmetic modulo 16 for all operations, including comparisons; see [RFC4340], Section 3.1, for more information on circular arithmetic ๐ŸŒ€. For reference, the DCCP generic header is as follows. (The diagram is repeated from [RFC4340], Section 5.1, which also shows the generic header with a 24-bit Sequence Number field.)

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |           Dest Port           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data Offset  | CCVal | CsCov |           Checksum            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Res | Type  |1|   Reserved    |  Sequence Number (high bits)  .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.                  Sequence Number (low bits)                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The CCVal field has enough space to express 4 round-trip times at quarter-RTT granularity. The sender MUST avoid wrapping CCVal on adjacent packets, as might happen, for example, if two data-carrying packets were sent 4 round-trip times apart with no packets intervening. Therefore, the sender SHOULD use the following algorithm for setting CCVal. The algorithm uses three variables: "last_WC" holds the last window counter value sent, "last_WC_time" is the time at which the first packet with window counter value "last_WC" was sent, and "RTT" is the current round-trip time estimate. last_WC is initialized to zero, and last_WC_time to the time of the first packet sent. Before sending a new packet, proceed like this:

   Let quarter_RTTs = floor((current_time - last_WC_time) / (RTT/4)).
   If quarter_RTTs > 0, then:
      Set last_WC := (last_WC + min(quarter_RTTs, 5)) mod 16.
      Set last_WC_time := current_time.
   Set the packet header's CCVal field to last_WC.

When this algorithm is used, adjacent data-carrying packets' CCVal counters never differ by more than five, modulo 16 ๐Ÿงฎ.

The window counter value may also change as feedback packets arrive. In particular, after receiving an acknowledgement for a packet sent with window counter WC, the sender SHOULD increase its window counter, if necessary, so that subsequent packets have window counter value at least (WC + 4) mod 16 ๐Ÿ’œ.

The CCVal counters are used by the receiver to determine whether multiple losses belong to a single loss event, to determine the interval to use for calculating the receive rate, and to determine when to send feedback packets. None of these procedures require the receiver to maintain an explicit estimate of the round-trip time. However, implementors who wish to keep such an RTT estimate may do so using CCVal. Let T(I) be the arrival time of the earliest valid received packet with CCVal = I. (Of course, when the window counter value wraps around to the same value mod 16, we must recalculate T(I).) Let D = 2, 3, or 4 and say that T(K) and T(K+D) both exist (packets were received with window counters K and K+D). Then the value (T(K+D) - T(K)) * 4/D MAY serve as an estimate of the round-trip time. Values of D = 4 SHOULD be preferred for RTT estimation. Concretely, say that the following packets arrived:

Time:       T1  T2  T3 T4  T5           T6  T7   T8  T9
       ------*---*---*-*----*------------*---*----*--*---->
CCVal:      K-1 K-1  K K   K+1          K+3 K+4  K+3 K+4

Then T7 - T3, the difference between the receive times of the first packet received with window counter K+4 and the first packet received with window counter K, is a reasonable round-trip time estimate. Because of the necessary constraint that measurements only come from packet pairs whose CCVals differ by at most 4, this procedure does not work when the inter-packet sending times are significantly greater than the RTT, resulting in packet pairs whose CCVals differ by 5. Explicit RTT measurement techniques, such as Timestamp and Timestamp Echo, should be used in that case ๐Ÿ•ฐ๏ธ.

8.2. Elapsed Time Options

The data receiver MUST include an elapsed time value on every required acknowledgement. This helps the sender distinguish between network round-trip time, which it must include in its rate equations, and delay at the receiver due to TFRC's infrequent acknowledgement rate, which it need not include ๐Ÿง . The receiver MUST at least include an Elapsed Time option on every feedback packet, but if at least one recent data packet (i.e., a packet received after the previous DCCP-Ack was sent) included a Timestamp option, then the receiver SHOULD include the corresponding Timestamp Echo option, with Elapsed Time value, as well. All of these option types are defined in the main DCCP specification [RFC4340] โฑ๏ธ.

8.3. Receive Rate Option

+--------+--------+--------+--------+--------+--------+
|11000010|00000110|            Receive Rate           |
+--------+--------+--------+--------+--------+--------+
 Type=194   Len=6

This option MUST be sent by the data receiver on all required acknowledgements. Its four data bytes indicate the rate at which the receiver has received data since it last sent an acknowledgement, in bytes per second. To calculate this receive rate, the receiver sets t to the larger of the estimated round-trip time and the time since the last Receive Rate option was sent. (Received data packets' window counters can be used to produce a suitable RTT estimate, as described in Section 8.1.) The receive rate then equals the number of data bytes received in the most recent t seconds, divided by t ๐Ÿ“ˆ.

Receive Rate options MUST NOT be sent on DCCP-Data packets, and any Receive Rate options on received DCCP-Data packets MUST be ignored. ๐Ÿšซ

8.4. Send Loss Event Rate Feature

The Send Loss Event Rate feature lets CCID 3 endpoints negotiate whether the receiver MUST provide Loss Event Rate options on its acknowledgements. DCCP A sends a "Change R(Send Loss Event Rate, 1)" option to ask DCCP B to send Loss Event Rate options as part of its acknowledgement traffic ๐Ÿค.

Send Loss Event Rate has feature number 192 and is server-priority. It takes one-byte Boolean values. DCCP B MUST send Loss Event Rate options on its acknowledgements when Send Loss Event Rate/B is one, although it MAY send Loss Event Rate options even when Send Loss Event Rate/B is zero. Values of two or more are reserved. A CCID 3 half-connection starts with Send Loss Event Rate equal to zero ๐Ÿ“.

8.5. Loss Event Rate Option

+--------+--------+--------+--------+--------+--------+
|11000000|00000110|          Loss Event Rate          |
+--------+--------+--------+--------+--------+--------+
 Type=192   Len=6

The option value indicates the inverse of the loss event rate, rounded UP, as calculated by the receiver. Its units are data packets per loss interval. Thus, if the Loss Event Rate option value is 100, then the loss event rate is 0.01 loss events per data packet (and the average loss interval contains 100 data packets). When each loss event has exactly one data packet loss, the loss event rate is the same as the data packet drop rate ๐Ÿ“‰.

See [RFC3448], Section 5, for a normative calculation of loss event rate. Before any losses have occurred, when the loss event rate is zero, the Loss Event Rate option value is set to "11111111111111111111111111111111" in binary (or, equivalently, to 2^32 - 1). The loss event rate calculation uses loss interval data lengths, as defined in Section 6.1.1 ๐ŸŒˆ.

Loss Event Rate options MUST NOT be sent on DCCP-Data packets, and any Loss Event Rate options on received DCCP-Data packets MUST be ignored. ๐Ÿ’–

8.6. Loss Intervals Option ๐Ÿฆ‹

   +--------+--------+--------+--------...--------+--------+---
   |11000001| Length |  Skip  |   Loss Interval   | More Loss
   |        |        | Length |                   | Intervals...
   +--------+--------+--------+--------...--------+--------+---
    Type=193                         9 bytes

Each 9-byte Loss Interval has three fields, like this:

     ____________________ Loss Interval _____________________
    /                                                        \
   +--------...-------+--------...--------+--------...--------+
   | Lossless Length  |E|   Loss Length   |    Data Length    |
   +--------...-------+--------...--------+--------...--------+
          3 bytes            3 bytes             3 bytes

Okay so the receiver tells the sender about the loss intervals it saw using a Loss Intervals option ๐Ÿ“ฆ. Section 6.1 explains what loss intervals are, so check that out too. This option MUST be sent by the data receiver on all required acknowledgements. It can report up to 28 loss intervals the receiver saw, but TFRC only actually uses the latest 9 of them ๐Ÿ‘€. This is how the sender works out a loss event rate and does a probabilistic check on the receiver's ECN Nonce Echo ๐Ÿ•ต๏ธโ€โ™€๏ธ.

The Loss Intervals option has a few jobs:

  • The sender uses it to calculate the loss event rate ๐Ÿ“Š.
  • Loss Intervals info is easy to check for consistency against earlier Loss Intervals options, and against any Loss Event Rate the receiver calculated itself ๐Ÿง.
  • The sender can probabilistically verify the ECN Nonce Echo for each Loss Interval, which makes misbehaving less likely ๐Ÿšซ.

Loss Intervals options MUST NOT be sent on DCCP-Data packets, and any Loss Intervals options on received DCCP-Data packets MUST be ignored. No exceptions, bestie ๐Ÿ’….

8.6.1. Option Details

The Loss Intervals option holds info about one to 28 consecutive loss intervals, and it always includes the most recent one โœจ. The intervals are listed in reverse chronological order, so newest first. If you need to report more than 28 loss intervals, you can send multiple Loss Intervals options, and the second one picks up exactly where the first one left off, and so on ๐Ÿ”. The options MUST contain information about at least the most recent NINTERVAL + 1 = 9 loss intervals, unless (1) there haven't been NINTERVAL + 1 loss intervals yet, or (2) the receiver knows, because of the sender's acknowledgements, that some earlier loss interval info already got received. In that second case, the receiver doesn't need to send the loss intervals the sender already knows about. The one exception is that it MUST transmit at least one loss interval no matter what ๐Ÿซถ.

The NINTERVAL parameter is equal to "n" as defined in [RFC3448], Section 5.4.

Loss interval sequence numbers are delta encoded, starting from the Acknowledgement Number ๐Ÿงฎ. So Loss Intervals options MUST NOT be sent on packets without an Acknowledgement Number, and any Loss Intervals options received on those packets MUST be ignored.

The first byte of the option data is the Skip Length, which tells you how many packets, up to and including the Acknowledgement Number, are not part of any Loss Interval ๐Ÿ™…โ€โ™€๏ธ. Like we talked about earlier, Skip Length must be less than or equal to NDUPACK = 3. If a packet has multiple Loss Intervals options, the Skip Lengths of the second and later options MUST equal zero, and any of those options with a nonzero Skip Length MUST be ignored.

After the Skip Length come the Loss Interval structures. Each Loss Interval has a Lossless Length, a Loss Length, an ECN Nonce Echo (E), and a Data Length ๐ŸŒธ.

  • Lossless Length is a 24-bit number that tells you how many packets are in the loss interval's lossless part. Heads up, this part can still contain lost or marked non-data packets ๐Ÿ™ˆ.
  • Loss Length is a 23-bit number that tells you how many packets are in the loss interval's lossy part. Lossless Length plus Loss Length gives you the loss interval's sequence length ๐Ÿง . Receivers SHOULD report the minimum valid Loss Length for each loss interval, so the first and last sequence numbers in each lossy part line up with lost or marked data packets ๐ŸŽฏ.
  • ECN Nonce Echo lives in the high-order bit of the 3-byte field that holds Loss Length. It's the one-bit sum (exclusive-or, or parity) of the data packet nonces received over the loss interval's lossless part, which is Lossless Length packets long ๐Ÿ”. If Lossless Length is 0, or the receiver is ECN Incapable, or the Lossless Length had no data packets in it, then the ECN Nonce Echo MUST be reported as 0. Also, any ECN nonces on received non-data packets MUST NOT contribute to the ECN Nonce Echo, so don't count them ๐Ÿšซ.
  • Data Length is a 24-bit number that tells you the loss interval's data length, as defined in Section 6.1.1 ๐Ÿ“.

8.6.2. Example

Let's look at a sequence of packets, where "-" is a safely delivered packet and "*" is a lost or marked one ๐Ÿ“.

   Sequence
    Numbers: 0         10        20        30        40  44
             |         |         |         |         |   |
             ----------*--------***-*--------*----------*-

Assuming packet 43 was lost, not marked, this sequence might get split into loss intervals like this ๐Ÿงฉ:

             0         10        20        30        40  44
             |         |         |         |         |   |
             ----------*--------***-*--------*----------*-
             \________/\_______/\___________/\_________/
                 L0       L1         L2          L3

A Loss Intervals option sent on a packet with Acknowledgement Number 44 to acknowledge this set of loss intervals might contain these bytes:

193,39,2, 0,0,10, 128,0,1, 0,0,10, 0,0,8, 0,0,5, 0,0,10, 0,0,8,
0,0,1, 0,0,8, 0,0,10, 128,0,0, 0,0,15

Here's how it breaks down, step by step ๐Ÿ”:

  • 193 is the Loss Intervals option number.
  • 39 is the length of the option, including the option type and length bytes. This option has info about (39 - 3)/9 = 4 loss intervals.
  • 2 is the Skip Length, so 2 packets. That means the most recent loss interval, L3, ends right before sequence number 44 - 2 + 1 = 43.
  • 0,0,10, 128,0,1, 0,0,10 defines L3. L3 has a 10-packet lossless part (0,0,10) preceded by a 1-packet lossy part. Counting backwards, the lossless part starts at sequence number 43 - 10 = 33, and the lossy part starts at sequence number 33 - 1 = 32. The ECN Nonce Echo for the lossless part (packets 33 through 42, inclusive) equals 1. The data length is 10, so the receiver believes the interval had exactly one non-data packet ๐Ÿค“.
  • 0,0,8, 0,0,5, 0,0,10 defines L2. Its lossless part starts at sequence number 32 - 8 = 24, its lossy part starts at sequence number 24 - 5 = 19, its ECN Nonce Echo (for packets [24,31]) equals 0, and its data length is 10 ๐Ÿ’ซ.
  • 0,0,8, 0,0,1, 0,0,8 defines L1. Its lossless part starts at sequence number 11, its lossy part starts at sequence number 10, its ECN Nonce Echo (for packets [11,18]) equals 0, and its data length is 8 ๐ŸŒท.
  • 0,0,10, 128,0,0, 0,0,15 defines L0. Its lossless part starts at sequence number 0, it has no lossy part, its ECN Nonce Echo (for packets [0,9]) equals 1, and its data length is 15. (This must be the first loss interval in the connection, because otherwise a data length bigger than the sequence length would be invalid ๐Ÿ™ƒ.)

9. Verifying Congestion Control Compliance with ECN โœจ

Okay, this part is about making sure the receiver is honest ๐Ÿ•ต๏ธโ€โ™€๏ธ. The sender can use the ECN Nonce Echoes from Loss Intervals options (and possibly from any Ack Vectors' ECN Nonce Echoes) to probabilistically check that the receiver is correctly reporting all dropped or marked packets. Even if ECN isn't used, meaning the receiver's ECN Incapable feature is set to one, the sender could still check on the receiver by sometimes not sending a packet, or by sending a packet out-of-order, to catch the receiver messing up its Loss Intervals or Ack Vector info ๐Ÿชค. Just a heads up, though: that isn't as robust or non-intrusive as the verification the ECN Nonce gives you ๐Ÿ˜ฌ.

9.1. Verifying the ECN Nonce Echo ๐Ÿ”

To verify the ECN Nonce Echo included with a Loss Intervals option, the sender keeps a table with the ECN nonce sum for each data packet ๐Ÿ“‹. As defined in [RFC3540], the nonce sum for sequence number S is the one-bit sum (exclusive-or, or parity) of data packet nonces over the sequence number range [I,S], where I is the initial sequence number. Let NonceSum(S) stand for this nonce sum for sequence number S, and define NonceSum(I - 1) as 0. Note that NonceSum doesn't count the nonces of non-data packets like DCCP-Ack ๐Ÿ™…โ€โ™€๏ธ. Then the Nonce Echo for an interval of packets with sequence numbers X to Y, inclusive, should equal this one-bit sum:

         NonceSum(X - 1) + NonceSum(Y)

An ECN Nonce Echo comes back for the lossless part of every Loss Interval, so a misbehaving receiver (one that reports a lost or marked data packet as "received non-marked" to dodge rate reductions ๐Ÿ˜ค) only has a 50% chance of guessing the right Nonce Echo for each loss interval ๐ŸŽฒ.

To verify the ECN Nonce Echo included with an Ack Vector option, the sender keeps a table with the ECN nonce value sent for each packet ๐Ÿ—‚๏ธ. The Ack Vector option explicitly says which packets were received non-marked, so the sender just adds up the nonces for those packets using a one-bit sum and compares the result to the Nonce Echo encoded in the Ack Vector's option type. Again, a misbehaving receiver only has a 50% chance of guessing an Ack Vector's correct Nonce Echo ๐Ÿ™ƒ. Alternatively, an Ack Vector's ECN Nonce Echo can be calculated from a table of ECN nonce sums instead of from ECN nonces. If the Ack Vector has lots of long runs of non-marked, non-dropped packets, the nonce sum-based calculation will probably be faster than a straight nonce-based calculation โšก.

Note that the Ack Vector's ECN Nonce Echo is measured over both data packets and non-data packets, while the Loss Intervals option reports ECN Nonce Echoes for data packets only. So you need different nonce sum tables to verify the two options ๐Ÿง.

9.2. Verifying the Reported Loss Intervals and Loss Event Rate

Okay so, besides probabilistically checking the ECN Nonce Echoes the receiver reports ๐Ÿ•ต๏ธโ€โ™€๏ธ, the sender can also verify the loss intervals and any loss event rate the receiver reports, if it wants to. The Loss Intervals option tells the sender the size of each loss interval exactly as the receiver saw it, so the sender can check that the receiver isn't wrongly squishing two loss events into one reported Loss Interval, using the window counter info it saved. The sender can also compare any Loss Event Rate option against the loss event rate it calculates from the Loss Intervals option. ๐Ÿ’…

Now, the numbers might not always match, and that's totally normal โœจ When a bunch of packets in a row get dropped, the receiver has no idea when those packets were sent, so it treats those losses as one loss event. The sender, on the other hand, may have saved the sending times or window counter info for those packets, so it can tell whether they were one loss event or several back-to-back ones. So with its knowledge of when the dropped packets were sent, the sender can work out a more accurate loss event rate ๐ŸŽฏ These kinds of differences SHOULD NOT be mistaken for the receiver being sus ๐Ÿ™ˆ

10. Implementation Issues

10.1. Timestamp Usage

CCID 3 data packets don't have to carry Timestamp options ๐Ÿซถ The sender can remember when its recent packets went out. Then the Acknowledgement Number and Elapsed Time option on each required acknowledgement give it enough info to work out the round trip time. Or, the sender MAY include Timestamp options on some of its data packets ๐ŸŒธ If it does, the receiver responds with Timestamp Echo options that include Elapsed Times, so the sender can figure out round-trip times without storing the timestamps of every packet it sent. Iconic efficiency, honestly ๐Ÿ‘‘

10.2. Determining Loss Events at the Receiver

The window counter is how the receiver figures out whether a bunch of lost packets all belong to the same loss event ๐Ÿง  The sender bumps the window counter up by one every quarter of a round-trip time. This section walks through exactly how the receiver uses the window counter to decide when two lost packets belong to the same loss event, step by step. Let's gooo ๐Ÿฆ‹

[RFC3448], Section 3.2.1 says each data packet has a timestamp, and offers an alternative where you use a "timestamp" that ticks up every quarter of an RTT, which is basically what the window counter does in CCID 3. But [RFC3448], Section 5.2 on "Translation from Loss History to Loss Events" is written in terms of timestamps, not window counters. So in this section, we give a method for going from loss history to loss events that's written entirely in terms of window counters ๐Ÿ’œ

Here's how the receiver decides whether two lost packets with sequence numbers X and Y belong to different loss events. Assume Y > X in circular sequence space. ๐Ÿงš

  • Let X_prev be the greatest valid sequence number received with X_prev < X.
  • Let Y_prev be the greatest valid sequence number received with Y_prev < Y.
  • Given a sequence number N, let C(N) be the window counter value associated with that packet.
  • Packets X and Y belong to different loss events if there exists a packet with sequence number S so that X_prev < S <= Y_prev, and the distance from C(X_prev) to C(S) is greater than 4. (The distance is the number D so that C(X_prev) + D = C(S) (mod WCTRMAX), where WCTRMAX is the maximum value for the window counter -- in our case, 16.)

So basically, the receiver only treats losses X and Y as separate loss events if there's some packet S received between X and Y where the distance from C(X_prev) to C(S) is greater than 4 ๐ŸŒˆ This messy-looking calculation is needed to handle the case where the window counter space wrapped all the way around between X and Y. When it doesn't wrap, the receiver can just check whether the distance from C(X_prev) to C(Y_prev) is greater than 4. If so, X and Y are separate loss events ๐ŸŽ€

Window counters can also help the receiver sort out multiple losses after a sudden drop in the actual round-trip time ๐Ÿ“‰ When the sender gets an acknowledgement for a data packet with window counter i, it bumps its own window counter up if needed, so that later data packets get window counter values of at least i+4. This can help cut down on errors where the receiver wrongly treats multiple loss events as one. ๐Ÿ’–

Note that if every packet between X and Y gets lost in the network, then X_prev and Y_prev are equal, and the receiver treats that whole run of consecutive losses as one loss event. Still, the sender won't get any DCCP-Ack packets during a stretch of consecutive losses, so it reduces its sending rate accordingly ๐Ÿ“

As an alternative to the window counter, the sender could send its round-trip time estimate directly to the receiver in a round-trip time option. The receiver would then use that estimate to figure out when multiple lost or marked packets belong in the same loss event. In some ways, a round-trip time option would encode the sender's RTT estimate more precisely than the window counter does โœจ But the window counter tells you about the relative sending times of packets, while a round-trip time option would only let the receiver compare relative receive times (when there are no timestamps). So the window counter performs more robustly when delay varies a lot for packets sent within one window of data ๐Ÿ˜ค Slightly more speculatively, a round-trip time option might be easier for middleboxes to use when they're trying to check that a flow is using conforming end-to-end congestion control ๐Ÿ‘€

10.3. Sending Feedback Packets

[RFC3448], Sections 6.1 and 6.2 say that the TFRC receiver must send a feedback packet when its newly calculated loss event rate p is greater than its previous value. CCID 3 follows this rule ๐Ÿ™Œ

Also, [RFC3448], Section 6.2, says the receiver uses a feedback timer to decide when to send extra feedback packets. If the feedback timer expires and data packets have come in since the last feedback was sent, the receiver sends a feedback packet. Each time the timer expires, the receiver resets it to expire after R_m seconds, where R_m is the most recent round-trip time estimate it got from the sender. CCID 3 receivers, though, usually use window counter values instead of a feedback timer to decide when to send extra feedback packets. This section explains how that works ๐Ÿง

Whenever the receiver sends a feedback message, it sets a local variable last_counter to the greatest window counter value received since the last feedback message was sent, if any data packets arrived since then. If the receiver gets a data packet with a window counter value greater than or equal to last_counter + 4, it sends a new feedback packet. ("Greater" and "greatest" are measured in circular window counter space.) ๐Ÿ”„

This procedure makes sure that when the sender is sending at less than one packet per round-trip time, the receiver sends a feedback packet after every data packet ๐Ÿ’… Similarly, when the sender is sending several packets per round-trip time, the receiver sends a feedback packet each time a data packet arrives with a window counter at least four higher than the window counter when the last feedback packet went out. So when the window counter is in use, the feedback timer isn't needed. ๐Ÿฆ‹

Still, the feedback timer could be handy in a few rare cases, to stop the sender from halving its sending rate for no good reason ๐Ÿ˜ญ You can actually build scenarios where a feedback timer at the receiver would stop the sender's nofeedback timer from expiring unnecessarily. Check out the case below, where a feedback packet is sent when a data packet arrives with window counter K.

      Window
      Counters: K   K+1 K+2 K+3 K+4 K+5 K+6  ...  K+15 K+16 K+17 ...
                |   |   |   |   |   |   |         |    |    |
      Data      |   |   |   |   |   |   |         |    |    |
      Packets   |   |   |   |   |   |   |         |    |    |
      Received:   - -  ---  -                ...   - - -- -  -- --  -
                  |                |               |    |    |        |
                  |                |               |    |    |        |
      Events:     1:               2:              3:   4:   5:       6:
                 "A"                              "B"  Timer "B"
                 sent                             sent       received
           1:  Feedback message A is sent.
           2:  A feedback message would have been sent if feedback
               timers had been used.

           3:  Feedback message B is sent.
           4:  Sender's nofeedback timer expires.
           5:  Feedback message B is received at the sender.
           6:  Sender's nofeedback timer would have expired if feedback
               timers had been used, and the feedback message at 2 had
               been sent.

The receiver gets data after the feedback packet was sent, but it hasn't received any data packets with a window counter between K+4 and K+14. A data packet with a window counter of K+4 or higher would have triggered a new feedback packet, but no feedback packet goes out until time 3. ๐Ÿฅฒ

The TFRC protocol says that after a feedback packet is received, the sender sets a nofeedback timer to at least four times the round-trip time estimate. If the sender doesn't get any feedback packets before the nofeedback timer expires, it halves its sending rate. In the figure, the sender gets feedback message A (time 1) and sets the nofeedback timer to expire roughly four round-trip times later (time 4). The sender starts sending again just before the nofeedback timer expires, but it doesn't get the resulting feedback message until after the timer has already gone off, so its sending rate gets halved for no reason ๐Ÿ˜ฉ If the connection had used feedback timers, the receiver would have sent a feedback message when the feedback timer expired at time 2, and that unnecessary halving would have been avoided โœจ

For implementors who want a feedback timer for the data receiver, we suggest estimating the round-trip time from the most recent data packet, as described in Section 8.1. Heads up, though: this procedure doesn't work when the inter-packet sending times are greater than the RTT. โš ๏ธ

11. Security Considerations ๐Ÿ”

okay bestie, security is serious business so I'm keeping this part crispy ๐Ÿ’…. Security stuff for DCCP lives in [RFC4340], and the security stuff for TFRC lives in [RFC3448], Section 9 ๐Ÿงš. For TFRC, the big worries are protecting against spoofed feedback ๐Ÿšจ and protecting the congestion control from wrong info coming from the receiver ๐Ÿ˜ญ. Main character problems fr ๐Ÿ’€.

In this document, I went really deep on how the sender can check the info the receiver sends back ๐Ÿ•ต๏ธโ€โ™€๏ธโœจ. When ECN is used, the receiver sends back ECN Nonce information to the sender ๐ŸŽ€. When ECN is NOT used, the sender can still try some techniques from Section 9 to catch the receiver messing up its congestion report ๐Ÿ‘€. Lowkey though, those are not as solid or as chill as the verification you get from the ECN Nonce ๐Ÿ™ˆ.

12. IANA Considerations ๐Ÿ“‹

ok so this is the paperwork part ๐Ÿ—‚๏ธ. This spec defines the value 3 in the DCCP CCID namespace that IANA manages ๐Ÿ’–. [RFC4340] mentions this assignment too ๐Ÿฆ‹.

CCID 3 also brings three sets of numbers that IANA should allocate ๐ŸŒˆ: CCID 3-specific Reset Codes, option types, and feature numbers ๐Ÿ“. These ranges keep future CCID 3-specific stuff from crowding DCCP's global namespaces ๐Ÿซถ. See [RFC4340], Section 10.3 for the details ๐Ÿ“–. But heads up, this document doesn't make any particular Reset Code allocations from the Reset Code range, except for experimental and testing use [RFC3692] ๐Ÿงช. We point to the Standards Action policy from [RFC2434] for how new stuff gets added ๐Ÿ”ฅ.

12.1. Reset Codes ๐Ÿ”„

Each entry in the DCCP CCID 3 Reset Code registry has three things ๐Ÿ’…: a CCID 3-specific Reset Code (a number from 128-255) ๐Ÿ”ข, a short description of it โœ๏ธ, and a reference to the RFC that defines it ๐Ÿ“š.

Reset Codes 184-190 and 248-254: permanently reserved for
                                 experimental and testing use
Reset Codes 128-183, 191-247, and 255: currently reserved, to be
                                       allocated with the Standards
                                       Action policy

Standards Action policy means IESG review and approval plus standards-track IETF RFC publication ๐ŸŽ“. Reset Codes 184-190 and 248-254 are permanently reserved for experimental and testing use ๐Ÿงช, and the rest (128-183, 191-247, and 255) are currently reserved and should be allocated with the Standards Action policy ๐Ÿ’–.

12.2. Option Types ๐ŸŽ

Each entry in the DCCP CCID 3 option type registry has a CCID 3-specific option type (a number from 128-255) ๐Ÿ”ข, the name of the option (like "Loss Intervals") ๐Ÿท๏ธ, and a reference to the RFC that defines it ๐Ÿ“š. The registry starts out filled with the values from Table 1, in Section 8 ๐Ÿ’…. This document allocates option types 192-194 โœจ, and option types 184-190 and 248-254 are permanently reserved for experimental and testing use ๐Ÿงช. The remaining option types -- 128-183, 191, 195-247, and 255 -- are currently reserved and should be allocated with the Standards Action policy, which needs IESG review and approval and standards-track IETF RFC publication ๐ŸŽ“.

12.3. Feature Numbers ๐Ÿงฉ

Each entry in the DCCP CCID 3 feature number registry has a CCID 3-specific feature number (a number from 128-255) ๐Ÿ”ข, the name of the feature (like "Send Loss Event Rate") ๐Ÿท๏ธ, and a reference to the RFC that defines the feature number ๐Ÿ“š. The registry starts out filled with the values from Table 2, in Section 8 ๐Ÿ’–. This document allocates feature number 192 ๐ŸŽ€, and feature numbers 184-190 and 248-254 are permanently reserved for experimental and testing use ๐Ÿงช. The remaining feature numbers -- 128-183, 191, 193-247, and 255 -- are currently reserved and should be allocated with the Standards Action policy, which needs IESG review and approval and standards-track IETF RFC publication ๐ŸŽ“.

13. Thanks ๐Ÿซถ

we owe huge thanks to Mark Handley for helping define CCID 3 ๐Ÿ‘‘. We also thank Mark Allman, Aaron Falk, Ladan Gharai, Sara Karlberg, Greg Minshall, Arun Venkataramani, David Vos, Yufei Wang, Magnus Westerlund, and the members of the DCCP Working Group for feedback on versions of this document ๐Ÿ’œ.

A. Appendix: Possible Future Changes to CCID 3 ๐Ÿ”ฎ

so this appendix is basically the "wish list" ๐Ÿช„. There are a bunch of spots where TFRC as specified in [RFC3448] doesn't match what DCCP users might want ๐Ÿฅบ. These include:

  1. The initial sending rate of at most four packets per RTT, as specified in [RFC3390] ๐Ÿฃ.
  2. The receiver sending an acknowledgement for every data packet it receives, when it receives at a rate less than one packet per round-trip time ๐Ÿ“ฌ.
  3. The sender being limited to at most doubling its sending rate from one round-trip time to the next (or, more specifically, limited to at most twice the reported receive rate over the previous round-trip time) ๐Ÿ™…โ€โ™€๏ธ.
  4. The limitation of halving the allowed sending rate after an idle period of four round-trip times (possibly down to the initial sending rate of two to four packets per round-trip time) ๐Ÿ˜ค.
  5. The response function used in [RFC3448], Section 3.1, which doesn't closely match TCP's behavior in environments with high packet drop rates [RFC3714] ๐Ÿ“‰.

One suggestion for higher initial sending rates is an initial sending rate of up to eight small packets per RTT, when the total packet size (headers included) is at most 4380 bytes ๐ŸŒŸ. The packets would be rate-paced out over a round-trip time instead of sent back-to-back like in TCP, so an initial sending rate of eight small packets per RTT with TFRC-based congestion control would be considerably milder than an initial window of eight small packets sent back-to-back in TCP ๐Ÿซง. As Section 5.1 describes, the initial sending rate also serves as a lower bound for reductions of the allowed sending rate during an idle period โฌ‡๏ธ.

We note that with CCID 3, the sender is in slow-start at the beginning and responds promptly to a reported packet loss or mark ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ. However, without feedback from the receiver, the sender can keep its old sending rate for up to four round-trip times โณ. One possibility would be that for an initial window of eight small packets, the initial nofeedback timer would be set to two round-trip times instead of four, so the sending rate would be reduced after two round-trips without feedback ๐Ÿง .

Research and engineering will be needed to look into the pros and cons of changing these limitations, in order to allow larger initial sending rates, to send fewer acknowledgements when the data sending rate is low, to allow more abrupt changes in the sending rate, or to allow a higher sending rate after an idle period ๐Ÿ”ฌ๐Ÿงช.

Normative References ๐Ÿ“š

  • [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
  • [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 2434, October 1998.
  • [RFC2581] Allman, M., Paxson, V., and W. Stevens, "TCP Congestion Control", RFC 2581, April 1999.
  • [RFC3168] Ramakrishnan, K., Floyd, S., and D. Black, "The Addition of Explicit Congestion Notification (ECN) to IP", RFC 3168, September 2001.
  • [RFC3390] Allman, M., Floyd, S., and C. Partridge, "Increasing TCP's Initial Window", RFC 3390, October 2002.
  • [RFC3448] Handley, M., Floyd, S., Padhye, J., and J. Widmer, "TCP Friendly Rate Control (TFRC): Protocol Specification", RFC 3448, January 2003.
  • [RFC3692] Narten, T., "Assigning Experimental and Testing Numbers Considered Useful", BCP 82, RFC 3692, January 2004.
  • [RFC4340] Kohler, E., Handley, M., and S. Floyd, "Datagram Congestion Control Protocol (DCCP)", RFC 4340, March 2006.

Informative References ๐Ÿ’ญ

  • [RFC3540] Spring, N., Wetherall, D., and D. Ely, "Robust Explicit Congestion Notification (ECN) Signaling with Nonces", RFC 3540, June 2003.
  • [RFC3714] Floyd, S. and J. Kempf, "IAB Concerns Regarding Congestion Control for Voice Traffic in the Internet", RFC 3714, March 2004.
  • [RFC4341] Floyd, S. and E. Kohler, "Profile for Datagram Congestion Control Protocol (DCCP) Congestion Control ID 2: TCP-like Congestion Control", RFC 4341, March 2006.
  • [V03] Arun Venkataramani, August 2003. Citation for acknowledgement purposes only.

Authors' Addresses ๐Ÿก

  • Sally Floyd ICSI Center for Internet Research 1947 Center Street, Suite 600 Berkeley, CA 94704 USA EMail: floyd@icir.org
  • Eddie Kohler 4531C Boelter Hall UCLA Computer Science Department Los Angeles, CA 90095 USA EMail: kohler@cs.ucla.edu
  • Jitendra Padhye Microsoft Research One Microsoft Way Redmond, WA 98052 USA EMail: padhye@microsoft.com

Full Copyright Statement ยฉ

Copyright (C) The Internet Society (2006).

This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.

This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Intellectual Property ๐Ÿงพ

The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.

Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.

The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.

Acknowledgement ๐Ÿ™Œ

Funding for the RFC Editor function is provided by the IETF Administrative Support Activity (IASA).

๐Ÿ“ฆ where this page comes from