tech-invite   World Map     

IETF     RFCs     Groups     SIP     ABNFs    |    3GPP     Specs     Glossaries     Architecture     IMS     UICC    |    search     info

RFC 7016

 
 
 

Adobe's Secure Real-Time Media Flow Protocol

Part 4 of 4, p. 75 to 113
Prev RFC Part

 


prevText      Top      Up      ToC       Page 75 
3.6.  Flows

   A flow is a unidirectional communication channel in a session for
   transporting a correlated series of user messages from a sender to a
   receiver.  Each end of a session may have zero or more sending flows
   to the other end.  Each sending flow at one end has a corresponding
   receiving flow at the other end.

3.6.1.  Overview

3.6.1.1.  Identity

   Flows are multiplexed in a session by a flow identifier.  Each end of
   a session chooses its sending flow identifiers independently of the
   other end.  The choice of similar flow identifiers by both ends does
   not imply an association.  A sender MAY choose any identifier for any
   flow; therefore, a flow receiver MUST NOT ascribe any semantic
   meaning, role, or name to a flow based only on its identifier.  There
   are no "well known" or reserved flow identifiers.

   Bidirectional flow association is indicated at flow startup with the
   Return Flow Association option (Section 2.3.11.1.2).  An endpoint can
   indicate that a new sending flow is in return (or response) to a
   receiving flow from the other end.  A sending flow MUST NOT indicate
   more than one return association.  A receiving flow can be specified
   as the return association for any number of sending flows.  The
   return flow association, if any, is fixed for the lifetime of the
   sending flow.  Note: Closure of one flow in an association does not
   automatically close other flows in the association, except as
   specified in Section 3.6.3.1.

Top      Up      ToC       Page 76 
   Flows are named with arbitrary user metadata.  This specification
   doesn't mandate any particular encoding, syntax, or semantics for the
   user metadata, except for the encoded size (Section 2.3.11.1.1); the
   user metadata is entirely reserved for the application.  The user
   metadata is fixed for the lifetime of the flow.

3.6.1.2.  Messages and Sequencing

   Flows provide message-oriented framing.  Large messages are
   fragmented for transport in the network.  Receivers reassemble
   fragmented messages and only present complete messages to the user.

   A sender queues messages on a sending flow one after another.  A
   receiver can recover the original queuing order of the messages, even
   when they are reordered in transit by the network or as a result of
   loss and retransmission, by means of the messages' fragment sequence
   numbers.  Flows are the basic units of message sequencing; each flow
   is sequenced independently of all other flows; inter-flow message
   arrival and delivery sequencing are not guaranteed.

   Independent flow sequencing allows a sender to prioritize the
   transmission or retransmission of the messages of one flow over those
   of other flows in a session, allocating capacity from the
   transmission budget according to priority.  RTMFP is designed for
   flows to be the basic unit of prioritization.  In any flow, fragment
   sequence numbers are unique and monotonically increasing; that is,
   the fragment sequence numbers for any message MUST be greater than
   the fragment sequence numbers of all messages previously queued in
   that flow.  Receipt of fragments out of sequence number order within
   a flow creates discontiguous gaps at the receiver, causing it to send
   an acknowledgement for every packet and also causing the size of the
   encoded acknowledgements to grow.  Therefore, for any flow, the
   sender SHOULD send lower sequence numbers first.

   A sender can abandon a queued message at any time, even if some
   fragments of that message have been received by the other end.  A
   receiver MUST be able to detect a gap in the flow when a message is
   abandoned; therefore, each message SHOULD take at least one sequence
   number from the sequence space even if no fragments for that message
   are ever sent.  The sender will transmit the fragments of all
   messages not abandoned, and retransmit any lost fragments of all
   messages not abandoned, until all the fragments of all messages not
   abandoned are acknowledged by the receiver.  A sender indicates a
   Forward Sequence Number (FSN) to instruct the receiver that sequence
   numbers less than or equal to the FSN will not be transmitted or
   retransmitted.  This allows the receiver to move forward over gaps
   and continue sequenced delivery of completely received messages to
   the user.  Any incomplete messages missing fragments with sequence

Top      Up      ToC       Page 77 
   numbers less than or equal to the FSN were abandoned by the sender
   and will never be completed.  A gap indication MUST be communicated
   to the receiving user.

3.6.1.3.  Lifetime

   A sender begins a flow by sending user message fragments to the other
   end, and including the user metadata and, if any, the return flow
   association.  The sender continues to include the user metadata and
   return flow association until the flow is acknowledged by the far
   end, at which point the sender knows that the receiver has received
   the user metadata and, if any, the return flow association.  After
   that point, the flow identifier alone is sufficient.

   Flow receivers SHOULD acknowledge all sequence numbers received for
   any flow, whether the flow is accepted or rejected.  Flow receivers
   MUST NOT acknowledge sequence numbers higher than the FSN that were
   not received.  Acknowledgements drive the congestion control and
   avoidance algorithms and therefore must be accurate.

   An endpoint can reject a receiving flow at any time in the flow's
   lifetime.  To reject the flow, the receiving endpoint sends a Flow
   Exception Report chunk (Section 2.3.16) immediately preceding every
   acknowledgement chunk for the rejected receiving flow.

   An endpoint may eventually conclude and close a sending flow.  The
   last sequence number of the flow is marked with the Final flag.  The
   sending flow is complete when all sequence numbers of the flow,
   including the final sequence number, have been cumulatively
   acknowledged by the receiver.  The receiving flow is complete when
   every sequence number from the FSN to the final sequence number has
   been received.  The sending flow and corresponding receiving flow at
   the respective ends hold the flow identifier of a completed flow in
   reserve for a time to allow delayed or duplicated fragments and
   acknowledgements to drain from the network without erroneously
   initiating a new receiving flow or erroneously acknowledging a new
   sending flow.

   If a flow sender receives a Flow Exception indication from the other
   end, the flow sender SHOULD close the flow and abandon all of the
   undelivered queued messages.  The flow sender SHOULD indicate an
   exception to the user.

Top      Up      ToC       Page 78 
3.6.2.  Sender

   Each sending flow comprises the flow-specific information context
   necessary to transfer that flow's messages to the other end.  Each
   sending flow context includes at least:

   o  F_FLOW_ID: this flow's identifier;

   o  STARTUP_OPTIONS: the set of options to send to the receiver until
      this flow is acknowledged, including the User's Per-Flow Metadata
      and, if set, the Return Flow Association;

   o  SEND_QUEUE: the unacknowledged message fragments queued in this
      flow, initially empty; each message fragment entry comprising the
      following:

      *  SEQUENCE_NUMBER: the sequence number of this fragment;

      *  DATA: this fragment's user data;

      *  FRA: the fragment control value for this message fragment,
         having one of the values enumerated for that purpose in
         Section 2.3.11 ("User Data Chunk");

      *  ABANDONED: a boolean flag indicating whether this fragment has
         been abandoned;

      *  SENT_ABANDONED: a boolean flag indicating whether this fragment
         was abandoned when sent;

      *  EVER_SENT: a boolean flag indicating whether this fragment has
         been sent at least once, initially false;

      *  NAK_COUNT: a count of the number of negative acknowledgements
         detected for this fragment, initially 0;

      *  IN_FLIGHT: a boolean flag indicating whether this fragment is
         currently outstanding, or in flight, in the network, initially
         false;

      *  TRANSMIT_SIZE: the size, in bytes, of the encoded User Data
         chunk (including the chunk header) for this fragment when it
         was transmitted into the network.

   o  F_OUTSTANDING_BYTES: the sum of the TRANSMIT_SIZE of each entry in
      SEND_QUEUE where entry.IN_FLIGHT is true;

Top      Up      ToC       Page 79 
   o  RX_BUFFER_SIZE: the most recent available buffer advertisement
      from the other end (Sections 2.3.13 and 2.3.14), initially
      65536 bytes;

   o  NEXT_SN: the next sequence number to assign to a message fragment,
      initially 1;

   o  F_FINAL_SN: the sequence number assigned to the final message
      fragment of the flow, initially having no value;

   o  EXCEPTION: a boolean flag indicating whether an exception has been
      reported by the receiver, initially false;

   o  The state, at any time being one of the following values: the open
      state F_OPEN; the closing states F_CLOSING and F_COMPLETE_LINGER;
      and the closed state F_CLOSED.

   Note: The following diagram is only a summary of state transitions
   and their causing events, and is not a complete operational
   specification.

                                 +--------+
                                 | F_OPEN |
                                 +--------+
                                      |CLOSE or
                                      |rcv Flow Exception
                                      |
                                      v
                                 +---------+
                                 |F_CLOSING|
                                 +---------+
                                      |rcv Data Ack
                                      |  0..F_FINAL_SN
                                      v
                             +-----------------+
                             |F_COMPLETE_LINGER|
                             +-----------------+
                                      | 130 seconds
                                      v
                                  +--------+
                                  |F_CLOSED|
                                  +--------+

                   Figure 19: Sending Flow State Diagram

Top      Up      ToC       Page 80 
3.6.2.1.  Startup

   The application opens a new sending flow to the other end in an
   S_OPEN session.  The implementation chooses a new flow ID that is not
   assigned to any other sending flow in that session in the F_OPEN,
   F_CLOSING, or F_COMPLETE_LINGER states.  The flow starts in the
   F_OPEN state.  The STARTUP_OPTIONS for the new flow is set with the
   User's Per-Flow Metadata (Section 2.3.11.1.1).  If this flow is in
   return (or response) to a receiving flow from the other end, that
   flow's ID is encoded in a Return Flow Association
   (Section 2.3.11.1.2) option and added to STARTUP_OPTIONS.  A new
   sending flow SHOULD NOT be opened in response to a receiving flow
   from the other end that is not in the RF_OPEN state when the sending
   flow is opened.

   At this point, the flow exists in the sender but not in the receiver.
   The flow begins when user data fragments are transmitted to the
   receiver.  A sender can begin a flow in the absence of immediate user
   data by sending a Forward Sequence Number Update (Section 3.6.2.7.1),
   by queuing and transmitting a user data fragment that is already
   abandoned.

3.6.2.2.  Queuing Data

   The application queues messages in an F_OPEN sending flow for
   transmission to the far end.  The implementation divides each message
   into one or more fragments for transmission in User Data chunks
   (Section 2.3.11).  Each fragment MUST be small enough so that, if
   assembled into a packet (Section 2.2.4) with a maximum-size common
   header, User Data chunk header, and, if not empty, this flow's
   STARTUP_OPTIONS, the packet will not exceed the path MTU
   (Section 3.5.4.3).

   For each fragment, create a fragment entry and set
   fragmentEntry.SEQUENCE_NUMBER to flow.NEXT_SN, and increment
   flow.NEXT_SN by one.  Set fragmentEntry.FRA according to the encoding
   in User Data chunks:

   0: This fragment is a complete message.

   1: This fragment is the first of a multi-fragment message.

   2: This fragment is the last of a multi-fragment message.

   3: This fragment is in the middle of a multi-fragment message.

   Append fragmentEntry to flow.SEND_QUEUE.

Top      Up      ToC       Page 81 
3.6.2.3.  Sending Data

   A sending flow is ready to transmit if the SEND_QUEUE contains at
   least one entry that is eligible to send, and if either
   RX_BUFFER_SIZE is greater than F_OUTSTANDING_BYTES or EXCEPTION is
   set to true.

   A SEND_QUEUE entry is eligible to send if it is not IN_FLIGHT, AND at
   least one of the following conditions holds:

   o  The entry is not ABANDONED; or

   o  The entry is the first one in the SEND_QUEUE; or

   o  The entry's SEQUENCE_NUMBER is equal to flow.F_FINAL_SN.

   If the session's transmission budget allows, a flow that is ready to
   transmit is selected for transmission according to the
   implementation's prioritization scheme.  The manner of flow
   prioritization is not mandated by this specification.

   Trim abandoned messages from the front of the queue, and find the
   Forward Sequence Number (FSN):

   1.  While the SEND_QUEUE contains at least two entries, AND the first
       entry is not IN_FLIGHT, AND the first entry is ABANDONED, remove
       and discard the first entry from the SEND_QUEUE;

   2.  If the first entry in the SEND_QUEUE is not abandoned, set FSN to
       entry.SEQUENCE_NUMBER - 1; otherwise,

   3.  If the first entry in the SEND_QUEUE is IN_FLIGHT, AND
       entry.SENT_ABANDONED is false, set FSN to
       entry.SEQUENCE_NUMBER - 1; otherwise,

   4.  The first entry in the SEND_QUEUE is abandoned and either is not
       IN_FLIGHT or was already abandoned when sent; set FSN to
       entry.SEQUENCE_NUMBER.

   The FSN MUST NOT be greater than any sequence number currently
   outstanding.  The FSN MUST NOT be equal to any sequence number
   currently outstanding that was not abandoned when sent.

Top      Up      ToC       Page 82 
   Assemble user data chunks for this flow into a packet to send to the
   receiver.  While enough space remains in the packet and the flow is
   ready to transmit:

   1.   Starting at the head of the SEND_QUEUE, find the first eligible
        fragment entry;

   2.   Encode the entry into a User Data chunk (Section 2.3.11) or, if
        possible (Section 3.6.2.3.2), a Next User Data chunk
        (Section 2.3.12);

   3.   If present, set chunk.flowID to flow.F_FLOW_ID;

   4.   If present, set chunk.sequenceNumber to entry.SEQUENCE_NUMBER;

   5.   If present, set chunk.fsnOffset to entry.SEQUENCE_NUMBER - FSN;

   6.   Set chunk.fragmentControl to entry.FRA;

   7.   Set chunk.abandon to entry.ABANDONED;

   8.   If entry.SEQUENCE_NUMBER equals flow.F_FINAL_SN, set chunk.final
        to true; else set chunk.final to false;

   9.   If any options are being sent with this chunk, set
        chunk.optionsPresent to true, assemble the options into the
        chunk, and assemble a Marker to terminate the option list;

   10.  If entry.ABANDONED is true, set chunk.userData to empty;
        otherwise, set chunk.userData to entry.DATA;

   11.  If adding the assembled chunk to the packet would cause the
        packet to exceed the path MTU, do not assemble this chunk into
        the packet; enough space no longer remains in the packet; stop.
        Otherwise, continue:

   12.  Set entry.IN_FLIGHT to true;

   13.  Set entry.EVER_SENT to true;

   14.  Set entry.NAK_COUNT to 0;

   15.  Set entry.SENT_ABANDONED to entry.ABANDONED;

   16.  Set entry.TRANSMIT_SIZE to the size of the assembled chunk,
        including the chunk header;

Top      Up      ToC       Page 83 
   17.  Assemble this chunk into the packet; and

   18.  If this flow or entry is considered Time Critical (real-time),
        set the timeCritical flag in the packet header (Section 2.2.4).

   Complete any other appropriate packet processing, and transmit the
   packet to the far end.

3.6.2.3.1.  Startup Options

   If STARTUP_OPTIONS is not empty, then when assembling the FIRST User
   Data chunk for this flow into a packet, add the encoded
   STARTUP_OPTIONS to that chunk's option list.

3.6.2.3.2.  Send Next Data

   The Next User Data chunk (Section 2.3.12) is a compact encoding for a
   user message fragment when multiple contiguous fragments are
   assembled into one packet.  Using this chunk where possible can
   conserve space in a packet, potentially reducing transmission
   overhead or allowing additional information to be sent in a packet.

   If, after assembling a user message fragment of a flow into a packet
   (Section 3.6.2.3), the next eligible fragment to be selected for
   assembly into that packet belongs to the same flow, AND its sequence
   number is one greater than that of the fragment just assembled, it is
   RECOMMENDED that an implementation encode a Next User Data chunk
   instead of a User Data chunk.

   The FIRST fragment of a flow assembled into a packet MUST be encoded
   as a User Data chunk.

3.6.2.4.  Processing Acknowledgements

   A Data Acknowledgement Bitmap chunk (Section 2.3.13) or a Data
   Acknowledgement Ranges chunk (Section 2.3.14) encodes the
   acknowledgement of receipt of one or more sequence numbers of a flow,
   as well as the receiver's current receive window advertisement.

   On receipt of an acknowledgement chunk for a sending flow:

   1.  Set PRE_ACK_OUTSTANDING_BYTES to flow.F_OUTSTANDING_BYTES;

   2.  Set flow.STARTUP_OPTIONS to empty;

   3.  Set flow.RX_BUFFER_SIZE to chunk.bufferBytesAvailable;

Top      Up      ToC       Page 84 
   4.  For each sequence number encoded in the acknowledgement, if
       there is an entry in flow.SEND_QUEUE with that sequence number
       and its IN_FLIGHT is true, then remove the entry from
       flow.SEND_QUEUE; and

   5.  Notify the congestion control and avoidance algorithms that
       PRE_ACK_OUTSTANDING_BYTES - flow.F_OUTSTANDING_BYTES were
       acknowledged.  Note that negative acknowledgements
       (Section 3.6.2.5) affect "TCP friendly" congestion control.

3.6.2.5.  Negative Acknowledgement and Loss

   A negative acknowledgement is inferred for an outstanding fragment if
   an acknowledgement is received for any other fragments sent after it
   in the same session.

   An implementation SHOULD consider a fragment to be lost once that
   fragment receives three negative acknowledgements.  A lost fragment
   is no longer outstanding in the network.

   The following describes an OPTIONAL method for detecting negative
   acknowledgements.

   Let the session track the order in which fragments are transmitted
   across all its sending flows by way of a monotonically increasing
   Transmission Sequence Number (TSN) recorded with each fragment queue
   entry each time that fragment is transmitted.

   Let the session information context contain additional variables:

   o  NEXT_TSN: the next TSN to record with a fragment's queue entry
      when it is transmitted, initially 1;

   o  MAX_TSN_ACK: the highest acknowledged TSN, initially 0.

   Let each fragment queue entry contain an additional variable TSN,
   initially 0, to track its transmission order.

   On transmission of a message fragment into the network, set its
   entry.TSN to session.NEXT_TSN, and increment session.NEXT_TSN.

   On acknowledgement of an outstanding fragment, if its entry.TSN is
   greater than session.MAX_TSN_ACK, set session.MAX_TSN_ACK to
   entry.TSN.

   After processing all acknowledgements in a packet containing at least
   one acknowledgement, then for each sending flow in that session, for
   each entry in that flow's SEND_QUEUE, if entry.IN_FLIGHT is true and

Top      Up      ToC       Page 85 
   entry.TSN is less than session.MAX_TSN_ACK, increment entry.NAK_COUNT
   and notify the congestion control and avoidance algorithms that a
   negative acknowledgement was detected in this packet.

   For each sending flow in that session, for each entry in that flow's
   SEND_QUEUE, if entry.IN_FLIGHT is true and entry.NAK_COUNT is at
   least 3, that fragment was lost in the network and is no longer
   considered to be in flight.  Set entry.IN_FLIGHT to false.  Notify
   the congestion control and avoidance algorithms of the loss.

3.6.2.6.  Timeout

   A fragment is considered lost and no longer in flight in the network
   if it has remained outstanding for at least ERTO.

   The following describes an OPTIONAL method to manage transmission
   timeouts.  This method REQUIRES that either burst avoidance
   (Section 3.5.2.3) is implemented or the implementation's congestion
   control and avoidance algorithms will eventually stop sending new
   fragments into the network if acknowledgements are persistently not
   received.

   Let the session information context contain an alarm TIMEOUT_ALARM,
   initially unset.

   On sending a packet containing at least one User Data chunk, set or
   reset TIMEOUT_ALARM to fire in ERTO.

   On receiving a packet containing at least one acknowledgement, reset
   TIMEOUT_ALARM (if already set) to fire in ERTO.

   When TIMEOUT_ALARM fires:

   1.  Set WAS_LOSS = false;

   2.  For each sending flow in the session, and for each entry in that
       flow's SEND_QUEUE:

       1.  If entry.IN_FLIGHT is true, set WAS_LOSS = true; and

       2.  Set entry.IN_FLIGHT to false.

   3.  If WAS_LOSS is true, perform ERTO backoff (Section 3.5.2.2); and

   4.  Notify the congestion control and avoidance algorithms of the
       timeout and, if WAS_LOSS is true, that there was loss.

Top      Up      ToC       Page 86 
3.6.2.7.  Abandoning Data

   The application can abandon queued messages at any time and for any
   reason.  Example reasons include (but are not limited to) the
   following: one or more fragments of a message have remained in the
   SEND_QUEUE for longer than a specified message lifetime; a fragment
   has been retransmitted more than a specified retransmission limit; a
   prior message on which this message depends (such as a key frame in a
   prediction chain) was abandoned and not delivered.

   To abandon a message fragment, set its SEND_QUEUE entry's ABANDON
   flag to true.  When abandoning a message fragment, abandon all
   fragments of the message to which it belongs.

   An abandoned fragment MUST NOT be un-abandoned.

3.6.2.7.1.  Forward Sequence Number Update

   Abandoned data may leave gaps in the sequence number space of a flow.
   Gaps may cause the receiver to hold completely received messages for
   ordered delivery to allow for retransmission of the missing
   fragments.  User Data chunks (Section 2.3.11) encode a Forward
   Sequence Number (FSN) to instruct the receiver that fragments with
   sequence numbers less than or equal to the FSN will not be
   transmitted or retransmitted.

   When the receiver has gaps in the received sequence number space and
   no non-abandoned message fragments remain in the SEND_QUEUE, the
   sender SHOULD transmit a Forward Sequence Number Update (FSN Update)
   comprising a User Data chunk marked abandoned, whose sequence number
   is the FSN and whose fsnOffset is 0.  An FSN Update allows the
   receiver to skip gaps that will not be repaired and deliver received
   messages to the user.  An FSN Update may be thought of as a
   transmission or retransmission of abandoned sequence numbers without
   actually sending the data.

   The method described in Section 3.6.2.3 ("Sending Data") generates
   FSN Updates when appropriate.

Top      Up      ToC       Page 87 
3.6.2.8.  Examples

    Sender
      |                   :
    1 |<---  Ack  ID=2, seq:0-16
    2 |--->  Data ID=2, seq#=25, fsnOff=9 (fsn=16)
    3 |--->  Data ID=2, seq#=26, fsnOff=10 (fsn=16)
    4 |<---  Ack  ID=2, seq:0-18
    5 |--->  Data ID=2, seq#=27, fsnOff=9 (fsn=18)
    6 |--->  Data ID=2, seq#=28, fsnOff=10 (fsn=18)
      |                   :

   There are 9 sequence numbers in flight with delayed acknowledgements.

                    Figure 20: Normal Flow with No Loss

Top      Up      ToC       Page 88 
    Sender
      |                   :
    1 |<---  Ack  ID=3, seq:0-30
    2 |--->  Data ID=3, seq#=45, fsnOff=15 (fsn=30)
    3 |<---  Ack  ID=3, seq:0-30, 32 (nack 31:1)
    4 |--->  Data ID=3, seq#=46, fsnOff=16 (fsn=30)
    5 |<---  Ack  ID=3, seq:0-30, 32, 34 (nack 31:2, 33:1)
    6 |<---  Ack  ID=3, seq:0-30, 32, 34-35 (nack 31:3=lost, 33:2)
    7 |--->  Data ID=3, seq#=47, fsnOff=15 (fsn=32, abandon 31)
    8 |<---  Ack  ID=3, seq:0-30, 32, 34-36 (nack 33:3=lost)
    9 |--->  Data ID=3, seq#=33, fsnOff=1 (fsn=32, retransmit 33)
   10 |<---  Ack  ID=3, seq:0-30, 32, 34-37
   11 |--->  Data ID=3, seq#=48, fsnOff=16 (fsn=32)
      |                   :
      |      (continues through seq#=59)
      |                   :
   12 |--->  Data ID=3, seq#=60, fsnOff=28(fsn=32)
   13 |<---  Ack  ID=3, seq:0-30, 34-46
   14 |--->  Data ID=3, seq#=61, fsnOff=29 (fsn=32)
   15 |<---  Ack  ID=3, seq:0-32, 34-47
   16 |--->  Data ID=3, seq#=62, fsnOff=30 (fsn=32)
   17 |<---  Ack  ID=3, seq:0-47
   18 |--->  Data ID=3, seq#=63, fsnOff=16 (fsn=47)
   19 |<---  Ack  ID=3, seq:0-49
   20 |--->  Data ID=3, seq#=64, fsnOff=15 (fsn=49)
      |                   :
   21 |<---  Ack  ID=3, seq:0-59
   22 |<---  Ack  ID=3, seq:0-59, 61 (nack 60:1)
   23 |<---  Ack  ID=3, seq:0-59, 61-62 (nack 60:2)
   24 |<---  Ack  ID=3, seq:0-59, 61-63 (nack 60:3=lost)
   25 |--->  Data ID=3, ABN=1, seq#=60, fsnOff=0 (fsn=60, abandon 60)
   26 |<---  Ack  ID=3, seq:0-59, 61-64
      |                   :
   27 |<---  Ack  ID=3, seq:0-64

   Flow with sequence numbers 31, 33, and 60 lost in transit, and a
   pause at 64.  33 is retransmitted; 31 and 60 are abandoned.  Note
   that line 25 is a Forward Sequence Number Update (Section 3.6.2.7.1).

                         Figure 21: Flow with Loss

Top      Up      ToC       Page 89 
3.6.2.9.  Flow Control

   The flow receiver advertises the amount of new data it's willing to
   accept from the flow sender with the bufferBytesAvailable derived
   field of an acknowledgement (Sections 2.3.13 and 2.3.14).

   The flow sender MUST NOT send new data into the network if
   flow.F_OUTSTANDING_BYTES is greater than or equal to the most
   recently received buffer advertisement, unless flow.EXCEPTION is true
   (Section 3.6.2.3).

3.6.2.9.1.  Buffer Probe

   The flow sender is suspended if the most recently received buffer
   advertisement is zero and the flow hasn't been rejected by the
   receiver -- that is, while RX_BUFFER_SIZE is zero AND EXCEPTION is
   false.  To guard against potentially lost acknowledgements that might
   reopen the receive window, a suspended flow sender SHOULD send a
   packet comprising a Buffer Probe chunk (Section 2.3.15) for this flow
   from time to time.

   If the receive window advertisement transitions from non-zero to
   zero, the flow sender MAY send a Buffer Probe immediately and SHOULD
   send a probe within one second.

   The initial period between Buffer Probes SHOULD be at least
   one second or ERTO, whichever is greater.  The period between probes
   SHOULD increase over time, but the period between probes SHOULD NOT
   be more than one minute or ERTO, whichever is greater.

   The flow sender SHOULD stop sending Buffer Probes if it is no longer
   suspended.

3.6.2.10.  Exception

   The flow receiver can reject the flow at any time and for any reason.
   The flow receiver sends a Flow Exception Report (Section 2.3.16) when
   it has rejected a flow.

   On receiving a Flow Exception Report for a sending flow:

   1.  If the flow is F_OPEN, close the flow (Section 3.6.2.11) and
       notify the user that the far end reported an exception with the
       encoded exception code;

   2.  Set the EXCEPTION flag to true; and

   3.  For each entry in SEND_QUEUE, set entry.ABANDONED = true.

Top      Up      ToC       Page 90 
3.6.2.11.  Close

   A sending flow is closed by the user or as a result of an exception.
   To close an F_OPEN flow:

   1.  Move to the F_CLOSING state;

   2.  If the SEND_QUEUE is not empty, AND the tail entry of the
       SEND_QUEUE has a sequence number of NEXT_SN - 1, AND the
       tail entry.EVER_SENT is false, set F_FINAL_SN to
       entry.SEQUENCE_NUMBER; else

   3.  The SEND_QUEUE is empty, OR the tail entry does not have a
       sequence number of NEXT_SN - 1, OR the tail entry.EVER_SENT is
       true: enqueue a new SEND_QUEUE entry with entry.SEQUENCE_NUMBER =
       flow.NEXT_SN, entry.FRA = 0, and entry.ABANDONED = true, and set
       flow.F_FINAL_SN to entry.SEQUENCE_NUMBER.

   An F_CLOSING sending flow is complete when its SEND_QUEUE transitions
   to empty, indicating that all sequence numbers, including the
   FINAL_SN, have been acknowledged by the other end.

   When an F_CLOSING sending flow becomes complete, move to the
   F_COMPLETE_LINGER state.

   A sending flow MUST remain in the F_COMPLETE_LINGER state for at
   least 130 seconds.  After at least 130 seconds, move to the F_CLOSED
   state.  The sending flow is now closed, its resources can be
   reclaimed, and its F_FLOW_ID MAY be used for a new sending flow.

3.6.3.  Receiver

   Each receiving flow comprises the flow-specific information context
   necessary to receive that flow's messages from the sending end and
   deliver completed messages to the user.  Each receiving flow context
   includes at least:

   o  RF_FLOW_ID: this flow's identifier;

   o  SEQUENCE_SET: the set of all fragment sequence numbers seen in
      this receiving flow, whether received or abandoned, initially
      empty;

   o  RF_FINAL_SN: the final fragment sequence number of the flow,
      initially having no value;

Top      Up      ToC       Page 91 
   o  RECV_BUFFER: the message fragments waiting to be delivered to the
      user, sorted by sequence number in ascending order, initially
      empty; each message fragment entry comprising the following:

      *  SEQUENCE_NUMBER: the sequence number of this fragment;

      *  DATA: this fragment's user data; and

      *  FRA: the fragment control value for this message fragment,
         having one of the values enumerated for that purpose in
         Section 2.3.11 ("User Data Chunk").

   o  BUFFERED_SIZE: the sum of the lengths of each fragment in
      RECV_BUFFER plus any additional storage overhead for the fragments
      incurred by the implementation, in bytes;

   o  BUFFER_CAPACITY: the desired maximum size for the receive buffer,
      in bytes;

   o  PREV_RWND: the most recent receive window advertisement sent in an
      acknowledgement, in 1024-byte blocks, initially having no value;

   o  SHOULD_ACK: whether or not an acknowledgement should be sent for
      this flow, initially false;

   o  EXCEPTION_CODE: the exception code to report to the sender when
      the flow has been rejected, initially 0;

   o  The state, at any time being one of the following values: the open
      state RF_OPEN; the closing states RF_REJECTED and
      RF_COMPLETE_LINGER; and the closed state RF_CLOSED.

Top      Up      ToC       Page 92 
   Note: The following diagram is only a summary of state transitions
   and their causing events, and is not a complete operational
   specification.

                                       +-+
                                       |X|
                                       +-+
                                        |rcv User Data for
                                        |  no existing flow
                                        v
                                   +---------+
                                   | RF_OPEN |
                                   +---------+
              rcv all sequence numbers|   |user reject,
                      0..RF_FINAL_SN  |   |rcv bad option,
                                      |   |no metadata at open,
                                      |   |association specified
                                      |   |  but not F_OPEN at open
                                  +---+   |
                                  |       v
                                  |  +-----------+
                                  |  |RF_REJECTED|
                                  |  +-----------+
                                  |       |rcv all sequence numbers
                                  |       |  0..RF_FINAL_SN
                                  v       v
                             +------------------+
                             |RF_COMPLETE_LINGER|
                             +------------------+
                                      | 120 seconds
                                      v
                                 +---------+
                                 |RF_CLOSED|
                                 +---------+

                  Figure 22: Receiving Flow State Diagram

Top      Up      ToC       Page 93 
3.6.3.1.  Startup

   A new receiving flow starts on receipt of a User Data chunk
   (Section 2.3.11) encoding a flow ID not belonging to any other
   receiving flow in the same session in the RF_OPEN, RF_REJECTED, or
   RF_COMPLETE_LINGER states.

   On receipt of such a User Data chunk:

   1.   Set temporary variables METADATA, ASSOCIATED_FLOWID, and
        ASSOCIATION to each have no value;

   2.   Create a new receiving flow context in this session, setting its
        RF_FLOW_ID to the flow ID encoded in the opening User Data
        chunk, and set to the RF_OPEN state;

   3.   If the opening User Data chunk encodes a User's Per-Flow
        Metadata option (Section 2.3.11.1.1), set METADATA to
        option.userMetadata;

   4.   If the opening User Data chunk encodes a Return Flow Association
        option (Section 2.3.11.1.2), set ASSOCIATED_FLOWID to
        option.flowID;

   5.   If METADATA has no value, the receiver MUST reject the flow
        (Section 3.6.3.7), moving it to the RF_REJECTED state;

   6.   If ASSOCIATED_FLOWID has a value, then if there is no sending
        flow in the same session with a flow ID of ASSOCIATED_FLOWID,
        the receiver MUST reject the flow, moving it to the RF_REJECTED
        state; otherwise, set ASSOCIATION to the indicated sending flow;

   7.   If ASSOCIATION indicates a sending flow, AND that sending flow's
        state is not F_OPEN, the receiver MUST reject this receiving
        flow, moving it to the RF_REJECTED state;

   8.   If the opening User Data chunk encodes any unrecognized option
        with a type code less than 8192 (Section 2.3.11.1), the receiver
        MUST reject the flow, moving it to the RF_REJECTED state;

   9.   If this new receiving flow is still RF_OPEN, then notify the
        user that a new receiving flow has opened, including the
        METADATA and, if present, the ASSOCIATION, and set
        flow.BUFFER_CAPACITY according to the user;

Top      Up      ToC       Page 94 
   10.  Perform the normal data processing (Section 3.6.3.2) for the
        opening User Data chunk; and

   11.  Set this session's ACK_NOW to true.

3.6.3.2.  Receiving Data

   A User Data chunk (Section 2.3.11) or a Next User Data chunk
   (Section 2.3.12) encodes one fragment of a user data message of a
   flow, as well as the flow's Forward Sequence Number and potentially
   optional parameters (Section 2.3.11.1).

   On receipt of a User Data or Next User Data chunk:

   1.   If chunk.flowID doesn't indicate an existing receiving flow in
        the same session in the RF_OPEN, RF_REJECTED, or
        RF_COMPLETE_LINGER state, perform the steps of Section 3.6.3.1
        ("Startup") to start a new receiving flow;

   2.   Retrieve the receiving flow context for the flow indicated by
        chunk.flowID;

   3.   Set flow.SHOULD_ACK to true;

   4.   If the flow is RF_OPEN, AND the chunk encodes any unrecognized
        option with a type code less than 8192 (Section 2.3.11.1), the
        flow MUST be rejected: notify the user of an exception, and
        reject the flow (Section 3.6.3.7), moving it to the RF_REJECTED
        state;

   5.   If the flow is not in the RF_OPEN state, set session.ACK_NOW
        to true;

   6.   If flow.PREV_RWND has a value and that value is less than
        2 blocks, set session.ACK_NOW to true;

   7.   If chunk.abandon is true, set session.ACK_NOW to true;

   8.   If flow.SEQUENCE_SET has any gaps (that is, if it doesn't
        contain every sequence number from 0 through and including the
        highest sequence number in the set), set session.ACK_NOW
        to true;

   9.   If flow.SEQUENCE_SET contains chunk.sequenceNumber, then this
        chunk is a duplicate: set session.ACK_NOW to true;

Top      Up      ToC       Page 95 
   10.  If flow.SEQUENCE_SET doesn't contain chunk.sequenceNumber, AND
        chunk.final is true, AND flow.RF_FINAL_SN has no value, then set
        flow.RF_FINAL_SN to chunk.sequenceNumber, and set
        session.ACK_NOW to true;

   11.  If the flow is in the RF_OPEN state, AND flow.SEQUENCE_SET
        doesn't contain chunk.sequenceNumber, AND chunk.abandon is
        false, then create a new RECV_BUFFER entry for this chunk's data
        and set entry.SEQUENCE_NUMBER to chunk.sequenceNumber,
        entry.DATA to chunk.userData, and entry.FRA to
        chunk.fragmentControl, and insert this new entry into
        flow.RECV_BUFFER;

   12.  Add to flow.SEQUENCE_SET the range of sequence numbers from 0
        through and including the chunk.forwardSequenceNumber derived
        field;

   13.  Add chunk.sequenceNumber to flow.SEQUENCE_SET;

   14.  If flow.SEQUENCE_SET now has any gaps, set session.ACK_NOW
        to true;

   15.  If session.ACK_NOW is false and session.DELACK_ALARM is not set,
        set session.DELACK_ALARM to fire in 200 milliseconds; and

   16.  Attempt delivery of completed messages in this flow's
        RECV_BUFFER to the user (Section 3.6.3.3).

   After processing all chunks in a packet containing at least one User
   Data chunk, increment session.RX_DATA_PACKETS by one.  If
   session.RX_DATA_PACKETS is at least two, set session.ACK_NOW to true.

   A receiving flow that is not in the RF_CLOSED state is ready to send
   an acknowledgement if its SHOULD_ACK flag is set.  Acknowledgements
   for receiving flows that are ready are sent either opportunistically
   by piggybacking on a packet that's already sending user data or an
   acknowledgement (Section 3.6.3.4.6), or when the session's ACK_NOW
   flag is set (Section 3.6.3.4.5).

3.6.3.3.  Buffering and Delivering Data

   A receiving flow's information context contains a RECV_BUFFER for
   reordering, reassembling, and holding the user data messages of the
   flow.  Only complete messages are delivered to the user; an
   implementation MUST NOT deliver partially received messages, except
   by special arrangement with the user.

Top      Up      ToC       Page 96 
   Let the Cumulative Acknowledgement Sequence Number (CSN) be the
   highest number in the contiguous range of numbers in SEQUENCE_SET
   starting with 0.  For example, if SEQUENCE_SET contains {0, 1, 2, 3,
   5, 6}, the contiguous range starting with 0 is 0..3, so the CSN is 3.

   A message is complete if all of its fragments are present in the
   RECV_BUFFER.  The fragments of one message have contiguous sequence
   numbers.  A message can be either a single fragment, whose fragment
   control value is 0-whole, or two or more fragments where the first's
   fragment control value is 1-begin, followed by zero or more fragments
   with control value 3-middle, and terminated by a last fragment with
   control value 2-end.

   An incomplete message segment is a contiguous sequence of one or more
   fragments that do not form a complete message -- that is, a 1-begin
   followed by zero or more 3-middle fragments but with no 2-end, or
   zero or more 3-middle fragments followed by a 2-end but with no
   1-begin, or one or more 3-middle fragments with neither a 1-begin nor
   a 2-end.

   Incomplete message segments can either be in progress or abandoned.
   An incomplete segment is abandoned in the following cases:

   o  The sequence number of the segment's first fragment is less than
      or equal to the CSN, AND that fragment's control value is not
      1-begin; or

   o  The sequence number of the segment's last fragment is less than
      the CSN.

   Abandoned message segments will never be completed, so they SHOULD be
   removed from the RECV_BUFFER to make room in the advertised receive
   window and the receiver's memory for messages that can be completed.

   The user can suspend delivery of a flow's messages.  A suspended
   receiving flow holds completed messages in its RECV_BUFFER until the
   user resumes delivery.  A suspended flow can cause the receive window
   advertisement to go to zero even when the BUFFER_CAPACITY is
   non-zero; this is described in detail in Section 3.6.3.5
   ("Flow Control").

   When the receiving flow is not suspended, the original queuing order
   of the messages is recovered by delivering, in ascending sequence
   number order, complete messages in the RECV_BUFFER whose sequence
   numbers are less than or equal to the CSN.

Top      Up      ToC       Page 97 
   The following describes a method for discarding abandoned message
   segments and delivering complete messages in original queuing order
   when the receiving flow is not suspended.

   While the first fragment entry in the RECV_BUFFER has a sequence
   number less than or equal to the CSN and delivery is still possible:

   1.  If entry.FRA is 0-whole, deliver entry.DATA to the user, and
       remove this entry from RECV_BUFFER; otherwise,

   2.  If entry.FRA is 2-end or 3-middle, this entry belongs to an
       abandoned segment, so remove and discard this entry from
       RECV_BUFFER; otherwise,

   3.  Entry.FRA is 1-begin.  Let LAST_ENTRY be the last RECV_BUFFER
       entry that is part of this message segment (LAST_ENTRY can be
       entry if the segment has only one fragment so far).  Then:

       1.  If LAST_ENTRY.FRA is 2-end, this segment is a complete
           message, so concatenate the DATA fields of each fragment
           entry of this segment in ascending sequence number order and
           deliver the complete message to the user, then remove the
           entries for this complete message from RECV_BUFFER;
           otherwise,

       2.  If LAST_ENTRY.SEQUENCE_NUMBER is less than CSN, this segment
           is incomplete and abandoned, so remove and discard the
           entries for this segment from RECV_BUFFER; otherwise,

       3.  LAST_ENTRY.SEQUENCE_NUMBER is equal to CSN and LAST_ENTRY.FRA
           is not 2-end: this segment is incomplete but still in
           progress.  Ordered delivery is no longer possible until at
           least one more fragment is received.  Stop.

   If flow.RF_FINAL_SN has a value and is equal to the CSN, AND
   RECV_BUFFER is empty, all complete messages have been delivered to
   the user, so notify the user that the flow is complete.

3.6.3.4.  Acknowledging Data

   A flow receiver SHOULD acknowledge all user data fragment sequence
   numbers seen in that flow.  Acknowledgements drive the sender's
   congestion control and avoidance algorithms, clear data from the
   sender's buffers, and in some sender implementations clock new data
   into the network; therefore, the acknowledgements must be accurate
   and timely.

Top      Up      ToC       Page 98 
3.6.3.4.1.  Timing

   For reasons similar to those discussed in Section 4.2.3.2 of RFC 1122
   [RFC1122], it is advantageous to delay sending acknowledgements for a
   short time, so that multiple data fragments can be acknowledged in a
   single transmission.  However, it is also advantageous for a sender
   to receive timely notification about the receiver's disposition of
   the flow, particularly in unusual or exceptional circumstances, so
   that the circumstances can be addressed if possible.

   Therefore, a flow receiver SHOULD send an acknowledgement for a flow
   as soon as is practical in any of the following circumstances:

   o  On receipt of a User Data chunk that starts a new flow;

   o  On receipt of a User Data or Next User Data chunk if the flow is
      not in the RF_OPEN state;

   o  On receipt of a User Data chunk where, before processing the
      chunk, the SEQUENCE_SET of the indicated flow does not contain
      every sequence number between 0 and the highest sequence number in
      the set (that is, if there was a sequence number gap before
      processing the chunk);

   o  On receipt of a User Data chunk where, after processing the chunk,
      the flow's SEQUENCE_SET does not contain every sequence number
      between 0 and the highest sequence number in the set (that is, if
      this chunk causes a sequence number gap);

   o  On receipt of a Buffer Probe for the flow;

   o  On receipt of a User Data chunk if the last acknowledgement sent
      for the flow indicated fewer than two bufferBlocksAvailable;

   o  On receipt of a User Data or Next User Data chunk for the flow if,
      after processing the chunk, the flow's BUFFER_CAPACITY is not at
      least 1024 bytes greater than BUFFERED_SIZE;

   o  On receipt of a User Data or Next User Data chunk for any sequence
      number that was already seen (that is, on receipt of a duplicate);

   o  On the first receipt of the final sequence number of the flow;

   o  On receipt of two packets in the session that contain user data
      for any flows since an acknowledgement was last sent, the new
      acknowledgements being for the flows having any User Data chunks
      in the received packets (that is, for every second packet
      containing user data);

Top      Up      ToC       Page 99 
   o  After receipt of a User Data chunk for the flow, if an
      acknowledgement for any other flow is being sent (that is,
      consolidate acknowledgements);

   o  After receipt of a User Data chunk for the flow, if any user data
      for a sending flow is being sent in a packet and if there is space
      available in the same packet (that is, attempt to piggyback an
      acknowledgement with user data if possible);

   o  No longer than 200 milliseconds after receipt of a User Data chunk
      for the flow.

3.6.3.4.2.  Size and Truncation

   Including an encoded acknowledgement in a packet might cause the
   packet to exceed the path MTU.  In that case:

   o  If the packet is being sent primarily to send an acknowledgement,
      AND this is the first acknowledgement in the packet, truncate the
      acknowledgement so that the packet does not exceed the path MTU;
      otherwise,

   o  The acknowledgement is being piggybacked in a packet with user
      data or with an acknowledgement for another flow: do not include
      this acknowledgement in the packet, and send it later.

3.6.3.4.3.  Constructing

   The Data Acknowledgement Bitmap chunk (Section 2.3.13) and Data
   Acknowledgement Ranges chunk (Section 2.3.14) encode a receiving
   flow's SEQUENCE_SET and its receive window advertisement.  The two
   chunks are semantically equivalent; implementations SHOULD send
   whichever provides the most compact encoding of the SEQUENCE_SET.

   When assembling an acknowledgement for a receiving flow:

   1.  If the flow's state is RF_REJECTED, first assemble a Flow
       Exception Report chunk (Section 2.3.16) for flow.flowID;

   2.  Choose the acknowledgement chunk type that most compactly encodes
       flow.SEQUENCE_SET;

   3.  Use the method described in Section 3.6.3.5 ("Flow Control") to
       determine the value for the acknowledgement chunk's
       bufferBlocksAvailable field.

Top      Up      ToC       Page 100 
3.6.3.4.4.  Delayed Acknowledgement

   As discussed in Section 3.6.3.4.1 ("Timing"), a flow receiver can
   delay sending an acknowledgement for up to 200 milliseconds after
   receiving user data.  The method described in Section 3.6.3.2
   ("Receiving Data") sets the session's DELACK_ALARM.

   When DELACK_ALARM fires, set ACK_NOW to true.

3.6.3.4.5.  Obligatory Acknowledgement

   One or more acknowledgements should be sent as soon as is practical
   when the session's ACK_NOW flag is set.  While the ACK_NOW flag
   is set:

   1.  Choose a receiving flow that is ready to send an acknowledgement;

   2.  If there is no such flow, there is no work to do, set ACK_NOW to
       false, set RX_DATA_PACKETS to 0, clear the DELACK_ALARM, and
       stop; otherwise,

   3.  Start a new packet;

   4.  Assemble an acknowledgement for the flow and include it in the
       packet, truncating it if necessary so that the packet doesn't
       exceed the path MTU;

   5.  Set flow.SHOULD_ACK to false;

   6.  Set flow.PREV_RWND to the bufferBlocksAvailable field of the
       included acknowledgement chunk;

   7.  Attempt to piggyback acknowledgements for any other flows that
       are ready to send an acknowledgement into the packet, as
       described below; and

   8.  Send the packet.

3.6.3.4.6.  Opportunistic Acknowledgement

   When sending a packet with user data or an acknowledgement, any other
   receiving flows that are ready to send an acknowledgement should
   include their acknowledgements in the packet if possible.

Top      Up      ToC       Page 101 
   To piggyback acknowledgements in a packet that is already being sent,
   where the packet contains user data or an acknowledgement, while
   there is at least one receiving flow that is ready to send an
   acknowledgement:

   1.  Assemble an acknowledgement for the flow;

   2.  If the acknowledgement cannot be included in the packet without
       exceeding the path MTU, the packet is full; stop.  Otherwise,

   3.  Include the acknowledgement in the packet;

   4.  Set flow.SHOULD_ACK to false;

   5.  Set flow.PREV_RWND to the bufferBlocksAvailable field of the
       included acknowledgement chunk; and

   6.  If there are no longer any receiving flows in the session that
       are ready to send an acknowledgement, set session.ACK_NOW to
       false, set session.RX_DATA_PACKETS to 0, and clear
       session.DELACK_ALARM.

3.6.3.4.7.  Example

   Figure 23 shows an example flow with sequence numbers 31 and 33 lost
   in transit; 31 is abandoned, and 33 is retransmitted.

   Receiver
    1 |<---  Data ID=3, seq#=29, fsnOff=11 (fsn=18)
    2 |<---  Data ID=3, seq#=30, fsnOff=12 (fsn=18)
    3 |--->  Ack  ID=3, seq:0-30
    4 |<---  Data ID=3, seq#=32, fsnOff=12 (fsn=20)
    5 |--->  Ack  ID=3, seq:0-30, 32
    6 |<---  Data ID=3, seq#=34, fsnOff=12 (fsn=22)
    7 |--->  Ack  ID=3, seq:0-30, 32, 34
      |                   :
    8 |<---  Data ID=3, seq#=46, fsnOff=16 (fsn=30)
    9 |--->  Ack  ID=3, seq:0-30, 32, 34-46
   10 |<---  Data ID=3, seq#=47, fsnOff=15 (fsn=32)
   11 |--->  Ack  ID=3, seq:0-32, 34-47
   12 |<---  Data ID=3, seq#=33, fsnOff=1 (fsn=32)
   13 |--->  Ack  ID=3, seq#=0-47
   14 |<---  Data ID=3, seq#=48, fsnOff=16 (fsn=32)
   15 |<---  Data ID=3, seq#=49, fsnOff=17 (fsn=32)
   16 |--->  Ack  ID=3, seq#=0-49
      |                   :

                     Figure 23: Flow Example with Loss

Top      Up      ToC       Page 102 
3.6.3.5.  Flow Control

   The flow receiver maintains a buffer for reassembling and reordering
   messages for delivery to the user (Section 3.6.3.3).  The
   implementation and the user may wish to limit the amount of resources
   (including buffer memory) that a flow is allowed to use.

   RTMFP provides a means for each receiving flow to govern the amount
   of data sent by the sender, by way of the bufferBytesAvailable
   derived field of acknowledgement chunks (Sections 2.3.13 and 2.3.14).
   This derived field indicates the amount of data that the sender is
   allowed to have outstanding in the network, until instructed
   otherwise.  This amount is also called the receive window.

   The flow receiver can suspend the sender by advertising a closed
   (zero length) receive window.

   The user can suspend delivery of messages from the receiving flow
   (Section 3.6.3.3).  This can cause the receive buffer to fill.

   In order for progress to be made on completing a fragmented message
   or repairing a gap for sequenced delivery in a flow, the flow
   receiver MUST advertise at least one buffer block in an
   acknowledgement if it is not suspended, even if the amount of data in
   the buffer exceeds the buffer capacity, unless the buffer capacity is
   0.  Otherwise, deadlock can occur, as the receive buffer will stay
   full and won't drain because of a gap or incomplete message, and the
   gap or incomplete message can't be repaired or completed because the
   sender is suspended.

   The receive window is advertised in units of 1024-byte blocks.  For
   example, advertisements for 1 byte, 1023 bytes, and 1024 bytes each
   require one block.  An advertisement for 1025 bytes requires
   two blocks.

   The following describes the RECOMMENDED method of calculating the
   bufferBlocksAvailable field of an acknowledgement chunk for a
   receiving flow:

   1.  If BUFFERED_SIZE is greater than or equal to BUFFER_CAPACITY, set
       ADVERTISE_BYTES to 0;

   2.  If BUFFERED_SIZE is less than BUFFER_CAPACITY, set
       ADVERTISE_BYTES to BUFFER_CAPACITY - BUFFERED_SIZE;

   3.  Set ADVERTISE_BLOCKS to CEIL(ADVERTISE_BYTES / 1024);

Top      Up      ToC       Page 103 
   4.  If ADVERTISE_BLOCKS is 0, AND BUFFER_CAPACITY is greater than 0,
       AND delivery to the user is not suspended, set ADVERTISE_BLOCKS
       to 1; and

   5.  Set the acknowledgement's bufferBlocksAvailable field to
       ADVERTISE_BLOCKS.

3.6.3.6.  Receiving a Buffer Probe

   A Buffer Probe chunk (Section 2.3.15) is sent by the flow sender
   (Section 3.6.2.9.1) to request the current receive window
   advertisement (in the form of an acknowledgement) from the flow
   receiver.

   On receipt of a Buffer Probe chunk:

   1.  If chunk.flowID doesn't belong to a receiving flow in the same
       session in the RF_OPEN, RF_REJECTED, or RF_COMPLETE_LINGER state,
       ignore this Buffer Probe; otherwise,

   2.  Retrieve the receiving flow context for the flow indicated by
       chunk.flowID; then

   3.  Set flow.SHOULD_ACK to true; and

   4.  Set session.ACK_NOW to true.

3.6.3.7.  Rejecting a Flow

   A receiver can reject an RF_OPEN flow at any time and for any reason.
   To reject a receiving flow in the RF_OPEN state:

   1.  Move to the RF_REJECTED state;

   2.  Discard all entries in flow.RECV_BUFFER, as they are no longer
       relevant;

   3.  If the user rejected the flow, set flow.EXCEPTION_CODE to the
       exception code indicated by the user; otherwise, the flow was
       rejected automatically by the implementation, so the exception
       code is 0;

   4.  Set flow.SHOULD_ACK to true; and

   5.  Set session.ACK_NOW to true.

Top      Up      ToC       Page 104 
   The receiver indicates that it has rejected a flow by sending a Flow
   Exception Report chunk (Section 2.3.16) with every acknowledgement
   (Section 3.6.3.4.3) for a flow in the RF_REJECTED state.

3.6.3.8.  Close

   A receiving flow is complete when every sequence number from 0
   through and including the final sequence number has been received --
   that is, when flow.RF_FINAL_SN has a value and flow.SEQUENCE_SET
   contains every sequence number from 0 through flow.RF_FINAL_SN,
   inclusive.

   When an RF_OPEN or RF_REJECTED receiving flow becomes complete, move
   to the RF_COMPLETE_LINGER state, set flow.SHOULD_ACK to true, and set
   session.ACK_NOW to true.

   A receiving flow SHOULD remain in the RF_COMPLETE_LINGER state for
   120 seconds.  After 120 seconds, move to the RF_CLOSED state.  The
   receiving flow is now closed, and its resources can be reclaimed once
   all complete messages in flow.RECV_BUFFER have been delivered to the
   user (Section 3.6.3.3).  The same flow ID might be used for a new
   flow by the sender after this point.

   Discussion: The flow sender detects that the flow is complete on
   receiving an acknowledgement of all fragment sequence numbers of the
   flow.  This can't happen until after the receiver has detected that
   the flow is complete and acknowledged all of the sequence numbers.
   The receiver's RF_COMPLETE_LINGER period is two minutes (one Maximum
   Segment Lifetime (MSL)); this period allows any in-flight packets to
   drain from the network without being misidentified and gives the
   sender an opportunity to retransmit any sequence numbers if the
   completing acknowledgement is lost.  The sender's F_COMPLETE_LINGER
   period is at least two minutes plus 10 seconds and doesn't begin
   until the completing acknowledgement is received; therefore, the same
   flow identifier won't be reused by the flow sender for a new sending
   flow for at least 10 seconds after the flow receiver has closed the
   receiving flow context.  This ensures correct operation independent
   of network delay, even when the sender's clock runs up to 8 percent
   faster than the receiver's.

4.  IANA Considerations

   This memo specifies chunk type code values (Section 2.3) and User
   Data option type code values (Section 2.3.11.1).  These type code
   values are assigned and maintained by Adobe.  Therefore, this memo
   has no IANA actions.

Top      Up      ToC       Page 105 
5.  Security Considerations

   This memo specifies a general framework that can be used to establish
   a confidential and authenticated session between endpoints.  A
   Cryptography Profile, not specified herein, defines the cryptographic
   algorithms, data formats, and semantics as used within this
   framework.  Designing a Cryptography Profile to ensure that
   communications are protected to the degree required by the
   application-specific threat model is outside the scope of this
   specification.

   A block cipher in CBC mode is RECOMMENDED for packet encryption
   (Section 2.2.3).  An attacker can predict the values of some fields
   from one plain RTMFP packet to the next or predict that some fields
   may be the same from one packet to the next.  This SHOULD be
   considered in choosing and implementing a packet encryption cipher
   and mode.

   The well-known Default Session Key of a Cryptography Profile serves
   multiple purposes, including the scrambling of session startup
   packets to protect interior fields from undesirable modification by
   middleboxes such as NATs, increasing the effort required for casual
   passive observation of startup packets, and allowing different
   applications of RTMFP using different Default Session Keys to
   (intentionally or not) share network transport addresses without
   interference.  The Default Session Key, being well known, MUST NOT be
   construed to contribute to the security of session startup; session
   startup is essentially in the clear.

   Section 3.5.4.2 describes an OPTIONAL method for processing a change
   of network address of a communicating peer.  Securely processing
   address mobility using that method, or any substantially similar
   method, REQUIRES at least that the packet encryption function of the
   Cryptography Profile (Section 2.2.3) employs a cryptographic
   verification mechanism comprising secret information known only to
   the two endpoints.  Without this constraint, that method, or any
   substantially similar method, becomes "session hijacking support".

   Flows and packet fragmentation imply semantics that could cause
   unbounded resource utilization in receivers, causing a denial of
   service.  Implementations SHOULD guard against unbounded or excessive
   resource use and abort sessions that appear abusive.

   A rogue but popular Redirector (Section 3.5.1.4) could direct session
   initiators to flood a victim address or network with Initiator Hello
   packets, potentially causing a denial of service.

Top      Up      ToC       Page 106 
   An attacker that can passively observe an IHello and that possesses a
   certificate matching the Endpoint Discriminator (without having to
   know the private key, if any, associated with it) can deny the
   initiator access to the desired responder by sending an RHello before
   the desired responder does, since only the first received RHello is
   selected by the initiator.  The attacker needn't forge the desired
   responder's source address, since the RHello is selected based on the
   tag echo and not the packet's source address.  This can simplify the
   attack in some network or host configurations.

   An attacker that can passively observe and record the packets of an
   established session can use traffic analysis techniques to infer the
   start and completion of flows without decrypting the packets.  The
   User Data fragments of flows have unique sequence numbers, so flows
   are immune to replay while they are open.  However, once a flow has
   completed and the linger period has concluded, the attacker could
   replay the recorded packets, opening a new flow in the receiver and
   duplicating the flow's data; this replay might have undesirable
   effects on the receiver's application.  The attacker could also infer
   that a new flow has begun reusing the recorded flow's identifier and
   replay the final sequence number or any of the other fragments in the
   flow, potentially denying or interfering with legitimate traffic to
   the receiver.  Therefore, the data integrity aspect of packet
   encryption SHOULD comprise anti-replay measures.

6.  Acknowledgements

   Special thanks go to Matthew Kaufman for his contributions to the
   creation and design of RTMFP.

   Thanks to Jari Arkko, Ben Campbell, Wesley Eddy, Stephen Farrell,
   Philipp Hancke, Bela Lubkin, Hilarie Orman, Richard Scheffenegger,
   and Martin Stiemerling for their detailed reviews of this memo.

Top      Up      ToC       Page 107 
7.  References

7.1.  Normative References

   [CBC]      Dworkin, M., "Recommendation for Block Cipher Modes of
              Operation", NIST Special Publication 800-38A,
              December 2001, <http://csrc.nist.gov/publications/
              nistpubs/800-38a/sp800-38a.pdf>.

   [RFC0768]  Postel, J., "User Datagram Protocol", STD 6, RFC 768,
              August 1980.

   [RFC0791]  Postel, J., "Internet Protocol", STD 5, RFC 791,
              September 1981.

   [RFC1122]  Braden, R., "Requirements for Internet Hosts -
              Communication Layers", STD 3, RFC 1122, October 1989.

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119, March 1997.

   [RFC2460]  Deering, S. and R. Hinden, "Internet Protocol, Version 6
              (IPv6) Specification", RFC 2460, December 1998.

   [RFC2914]  Floyd, S., "Congestion Control Principles", BCP 41,
              RFC 2914, September 2000.

   [RFC4821]  Mathis, M. and J. Heffner, "Packetization Layer Path MTU
              Discovery", RFC 4821, March 2007.

   [RFC5681]  Allman, M., Paxson, V., and E. Blanton, "TCP Congestion
              Control", RFC 5681, September 2009.

7.2.  Informative References

   [RFC5389]  Rosenberg, J., Mahy, R., Matthews, P., and D. Wing,
              "Session Traversal Utilities for NAT (STUN)", RFC 5389,
              October 2008.

   [ScalableTCP]
              Kelly, T., "Scalable TCP: Improving Performance in
              Highspeed Wide Area Networks", December 2002,
              <http://datatag.web.cern.ch/datatag/papers/
              pfldnet2003-ctk.pdf>.

Top      Up      ToC       Page 108 
Appendix A.  Example Congestion Control Algorithm

   As mandated in Section 3.5.2, an RTMFP is required to use TCP-
   compatible congestion control, but flexibility in exact
   implementation is allowed, within certain limits.  This section
   describes an experimental window-based congestion control algorithm
   that is appropriate for real-time and bulk data transport in RTMFP.
   The algorithm includes slow start and congestion avoidance phases,
   including modified increase and decrease parameters.  These
   parameters are further adjusted according to whether real-time data
   is being sent and whether Time Critical Reverse Notifications are
   received.

A.1.  Discussion

   RFC 5681 defines the standard window-based congestion control
   algorithms for TCP.  These algorithms are appropriate for delay-
   insensitive bulk data transport but have undesirable behaviors for
   delay- and loss-sensitive applications.  Among the undesirable
   behaviors are the cutting of the congestion window in half during a
   loss event, and the rapidity of the slow start algorithm's
   exponential growth.  Cutting the congestion window in half requires a
   large channel headroom to support a real-time application and can
   cause a large amount of jitter from sender-side buffering.  Doubling
   the congestion window during the slow start phase can lead to the
   congestion window temporarily growing to twice the size it should be,
   causing a period of excessive loss in the path.

   We found that a number of deployed TCP implementations use the method
   of equation (3) from Section 3.1 of RFC 5681; this method, when
   combined with the recommended behavior of acknowledging every other
   packet, causes the congestion window to grow at approximately half
   the rate that the recommended method specifies.  In order to compete
   fairly with these deployed TCPs, we choose 768 bytes per round trip
   as the increment during the normal congestion avoidance phase; this
   is approximately half of the typical maximum segment size of
   1500 bytes and is also easily subdivided.

   The sender may be sending real-time data to the far end.  When
   sending real-time data, a smoother response to congestion is desired
   while still competing with reasonable fairness to other flows in the
   Internet.  In order to scale the sending rate quickly, the slow start
   algorithm is desired, but slow start's normal rate of increase can
   cause excessive loss in the last round trip.  Accordingly, slow
   start's exponential increase rate is adjusted to double approximately
   every 3 round trips instead of every round trip.  The multiplicative
   decrease cuts the congestion window by one eighth on loss to maintain
   a smoother sending rate.  The additive increase is done at half the

Top      Up      ToC       Page 109 
   normal rate (incrementing at 384 bytes per round trip), to both
   compensate for the less aggressive loss response and probe the path
   capacity more gently.

   The far end may report that it is receiving real-time data from other
   peers, or the sender may be sending real-time data to other far ends.
   In these circumstances (if not sending real-time data to this far
   end), it is desirable to respond differently than the standard TCP
   algorithms specify, to both yield capacity to the real-time flows and
   avoid excessive losses while probing the path capacity.  Slow start's
   exponential increase is disabled, and the additive increase is done
   at half the normal rate (incrementing at 384 bytes per round trip).
   Multiplicative decrease is left at the normal rate (cutting by half)
   to yield to other flows.

   Since real-time messages may be small, and sent regularly, it is
   advantageous to spread congestion window increases out across the
   round-trip time instead of doing them all at once.  We divide the
   round trip into 16 segments with an additive increase of a useful
   size (48 bytes) per segment.

   Scalable TCP [ScalableTCP] describes experimental methods of
   modifying the additive increase and multiplicative decrease of the
   congestion window in large delay-bandwidth scenarios.  The congestion
   window is increased by 1% each round trip and decreased by one eighth
   on loss in the congestion avoidance phase in certain circumstances
   (specifically, when a 1% increase is larger than the normal additive-
   increase amount).  Those methods are adapted here.  The scalable
   increase amount is 48 bytes for every 4800 bytes acknowledged, to
   spread the increase out over the round trip.  The congestion window
   is decreased by one eighth on loss when it is at least 67200 bytes
   per round trip, which is seven eighths of 76800 (the point at which
   1% is greater than 768 bytes per round trip).  When sending real-time
   data to the far end, the scalable increase is 1% or 384 bytes per
   round trip, whichever is greater.  Otherwise, when notified that the
   far end is receiving real-time data from other peers, the scaled
   increase is adjusted to 0.5% or 384 bytes per round trip, whichever
   is greater.

Top      Up      ToC       Page 110 
A.2.  Algorithm

   Let SMSS denote the Sender Maximum Segment Size [RFC5681], for
   example 1460 bytes.  Let CWND_INIT denote the Initial Congestion
   Window (IW) according to Section 3.1 of RFC 5681, for example
   4380 bytes.  Let CWND_TIMEDOUT denote the congestion window after a
   timeout indicating lost data, being 1*SMSS (for example, 1460 bytes).

   Let the session information context contain additional variables:

   o  CWND: the congestion window, initialized to CWND_INIT;

   o  SSTHRESH: the slow start threshold, initialized to positive
      infinity;

   o  ACKED_BYTES_ACCUMULATOR: a count of acknowledged bytes,
      initialized to 0;

   o  ACKED_BYTES_THIS_PACKET: a count of acknowledged bytes observed in
      the current packet;

   o  PRE_ACK_OUTSTANDING: the number of bytes outstanding in the
      network before processing any acknowledgements in the current
      packet;

   o  ANY_LOSS: an indication of whether any loss has been detected in
      the current packet;

   o  ANY_NAKS: an indication of whether any negative acknowledgements
      have been detected in the current packet;

   o  ANY_ACKS: an indication of whether any acknowledgement chunks have
      been received in the current packet.

   Let FASTGROW_ALLOWED indicate whether the congestion window is
   allowed to grow at the normal rate versus a slower rate, being false
   if a Time Critical Reverse Notification has been received on this
   session within the last 800 milliseconds (Sections 2.2.4 and 3.5.2.1)
   or if a Time Critical Forward Notification has been sent on ANY
   session in the last 800 milliseconds, and otherwise being true.

   Let TC_SENT indicate whether a Time Critical Forward Notification has
   been sent on this session within the last 800 milliseconds.

   Implement the method described in Section 3.6.2.6 to manage
   transmission timeouts, including setting the TIMEOUT_ALARM.

Top      Up      ToC       Page 111 
   On being notified that the TIMEOUT_ALARM has fired, perform the
   function shown in Figure 24:

   on TimeoutNotification(WAS_LOSS):
       set SSTHRESH to MAX(SSTHRESH, CWND * 3/4).
       set ACKED_BYTES_ACCUMULATOR to 0.
       if WAS_LOSS is true:
           set CWND to CWND_TIMEDOUT.
       else:
           set CWND to CWND_INIT.

         Figure 24: Pseudocode for Handling a Timeout Notification

   Before processing each received packet in this session:

   1.  Set ANY_LOSS to false;

   2.  Set ANY_NAKS to false;

   3.  Set ACKED_BYTES_THIS_PACKET to 0; and

   4.  Set PRE_ACK_OUTSTANDING to S_OUTSTANDING_BYTES.

   On notification of loss (Section 3.6.2.5), set ANY_LOSS to true.

   On notification of negative acknowledgement (Section 3.6.2.5), set
   ANY_NAKS to true.

   On notification of acknowledgement of data (Section 3.6.2.4), set
   ANY_ACKS to true, and add the count of acknowledged bytes to
   ACKED_BYTES_THIS_PACKET.

Top      Up      ToC       Page 112 
   After processing all chunks in each received packet for this session,
   perform the function shown in Figure 25:

   if ANY_LOSS is true:
       if (TC_SENT is true) OR (PRE_ACK_OUTSTANDING > 67200 AND \
       FASTGROW_ALLOWED is true):
           set SSTHRESH to MAX(PRE_ACK_OUTSTANDING * 7/8, CWND_INIT).
       else:
           set SSTHRESH to MAX(PRE_ACK_OUTSTANDING * 1/2, CWND_INIT).
       set CWND to SSTHRESH.
       set ACKED_BYTES_ACCUMULATOR to 0.
   else if (ANY_ACKS is true) AND (ANY_NAKS is false) AND \
   (PRE_ACK_OUTSTANDING >= CWND):
       set var INCREASE to 0.
       var AITHRESH.
       if FASTGROW_ALLOWED is true:
           if CWND < SSTHRESH:
               set INCREASE to ACKED_BYTES_THIS_PACKET.
           else:
               add ACKED_BYTES_THIS_PACKET to ACKED_BYTES_ACCUMULATOR.
               set AITHRESH to MIN(MAX(CWND / 16, 64), 4800).
               while ACKED_BYTES_ACCUMULATOR >= AITHRESH:
                   subtract AITHRESH from ACKED_BYTES_ACCUMULATOR.
                   add 48 to INCREASE.
       else FASTGROW_ALLOWED is false:
           if CWND < SSTHRESH AND TC_SENT is true:
               set INCREASE to CEIL(ACKED_BYTES_THIS_PACKET / 4).
           else:
               var AITHRESH_CAP.
               if TC_SENT is true:
                   set AITHRESH_CAP to 2400.
               else:
                   set AITHRESH_CAP to 4800.
               add ACKED_BYTES_THIS_PACKET to ACKED_BYTES_ACCUMULATOR.
               set AITHRESH to MIN(MAX(CWND / 16, 64), AITHRESH_CAP).
               while ACKED_BYTES_ACCUMULATOR >= AITHRESH:
                   subtract AITHRESH from ACKED_BYTES_ACCUMULATOR.
                   add 24 to INCREASE.
       set CWND to MAX(CWND + MIN(INCREASE, SMSS), CWND_INIT).

          Figure 25: Pseudocode for Congestion Window Adjustment
                         after Processing a Packet

Top      Up      ToC       Page 113 
Author's Address

   Michael C. Thornburgh
   Adobe Systems Incorporated
   345 Park Avenue
   San Jose, CA  95110-2704
   US

   Phone: +1 408 536 6000
   EMail: mthornbu@adobe.com
   URI:   http://www.adobe.com/