<?xml version="1.0" encoding="US-ASCII"?>
<?xml-stylesheet type='text/xsl' href='include/rfc2629.xslt' ?>

<!-- xml2rfc v2 -->

<?rfc symrefs="yes"?>
<?rfc toc="yes"?>
<?rfc tocdepth="8"?>
<?rfc rfcedstyle="yes"?>
<?rfc compact="yes"?>
<?rfc subcompact="no"?>

<!DOCTYPE rfc SYSTEM "include/rfc2629.dtd" [
<!ENTITY rfc768 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.0768.xml">
<!ENTITY rfc791 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.0791.xml">
<!ENTITY rfc793 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.0793.xml">
<!ENTITY rfc1122 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.1122.xml">
<!ENTITY rfc2119 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.2119.xml">
<!ENTITY rfc2460 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.2460.xml">
<!ENTITY rfc2914 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.2914.xml">
<!ENTITY rfc3235 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.3235.xml">
<!ENTITY rfc3436 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.3436.xml">
<!ENTITY rfc3550 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.3550.xml">
<!ENTITY rfc3711 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.3711.xml">
<!ENTITY rfc3758 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.3758.xml">
<!ENTITY rfc4347 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4347.xml">
<!ENTITY rfc4568 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4568.xml">
<!ENTITY rfc4585 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4585.xml">
<!ENTITY rfc4821 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4821.xml">
<!ENTITY rfc4960 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4960.xml">
<!ENTITY rfc5245 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.5245.xml">
<!ENTITY rfc5246 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.5246.xml">
<!ENTITY rfc5389 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.5389.xml">
<!ENTITY rfc5681 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.5681.xml">
<!ENTITY rfc5761 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.5761.xml">
<!ENTITY rfc5764 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.5764.xml">
<!ENTITY rfc6083 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.6083.xml">
<!ENTITY rfc6525 PUBLIC '' "http://xml.resource.org/public/rfc/bibxml/reference.RFC.6525.xml">
]>

<rfc category="info" ipr="noModificationTrust200902" number="7016" submissionType="IETF" consensus="no">

<front>
  <title abbrev="Adobe RTMFP">Adobe's Secure Real-Time Media Flow Protocol</title>

        <author initials="M.C." surname="Thornburgh" fullname="Michael C. Thornburgh">
           <organization abbrev="Adobe">Adobe Systems Incorporated</organization>
             <address>
               <postal>
                <street>345 Park Avenue</street>
                <city>San Jose</city>
                <region>CA</region>
                <code>95110-2704</code>
                <country>US</country>
               </postal>
               <phone>+1 408 536 6000</phone>
               <email>mthornbu@adobe.com</email>
               <uri>http://www.adobe.com/</uri>
            </address>
        </author>

        <date month="November" year="2013"/>
        <area>Transport</area>

        <keyword>RTMFP</keyword>

     <abstract>
         <t>
         This memo describes Adobe's Secure Real-Time Media Flow
         Protocol (RTMFP), an endpoint-to-endpoint communication
         protocol designed to securely transport parallel flows of
         real-time video, audio, and data messages, as well as bulk
         data, over IP networks.  RTMFP has features that make it effective
         for peer-to-peer (P2P) as well as client-server communications,
         even when Network Address Translators (NATs) are used.</t>
      </abstract>
</front>

<middle>

<section anchor="intro" title="Introduction">

<t>Adobe's Secure Real-Time Media Flow Protocol (RTMFP) is intended for use as
a general purpose endpoint-to-endpoint data transport service in IP networks.
It has features that make it well suited to the transport of real-time media
(such as low-delay video, audio, and data) as well as bulk data, and for
client-server as well as peer-to-peer (P2P) communication.  These features
include independent parallel message flows that may have different delivery
priorities, variable message reliability (from TCP-like full reliability to
UDP-like best effort), multi-point congestion control, and built-in security.
Session multiplexing and facilities to support UDP hole-punching simplify
Network Address Translator (NAT) traversal in peer-to-peer systems.</t>

<t>RTMFP is implemented in Flash Player, Adobe Integrated Runtime (AIR), and
Adobe Media Server (AMS, formerly Flash Media Server or FMS), all from Adobe
Systems Incorporated, and is used as the foundation transport protocol for
real-time video, audio, and data communication, both client-server and P2P,
in those products. At the time of writing, the Adobe Flash Player runtime is
installed on more than one billion end-user desktop computers.</t>

<t>RTMFP was developed by Adobe Systems Incorporated and is not the product
of an IETF activity.</t>

<t>This memo describes the syntax and operation of the Secure Real-Time Media
Flow Protocol.</t>

<t>This memo describes a general security framework that, when combined with
an application-specific Cryptography Profile, can be used to establish a
confidential and authenticated session between endpoints.  The application-specific
Cryptography Profile, not defined herein, would detail the specific cryptographic
algorithms, data formats, and semantics to be used within this framework.
Interoperation between applications of RTMFP requires common or compatible
Cryptography Profiles.</t>

<t>Note to implementers: at the time of writing, the Cryptography Profile
used by the above-mentioned Adobe products is not publicly described by Adobe.
Implementers should investigate the availability of documentation of that
Cryptography Profile prior to implementing RTMFP for the purpose of
interoperation with the above-mentioned Adobe products.</t>

<section title="Design Highlights of RTMFP">

<t>Between any pair of communicating endpoints is a single, bidirectional,
secured, congestion controlled session. Unidirectional flows convey messages
from one end to the other within the session. An endpoint can have concurrent
sessions with multiple other far endpoints.</t>

<t>
Design highlights of RTMFP include the following:
<list style="symbols">
        <t>The security framework is an inherent part of the basic protocol.
        The application designer chooses the cryptographic formats and
        algorithms to suit the needs of the application, and may update them
        as the state of the security arts progresses.</t>

        <t>Cryptographic Endpoint Discriminators can resist port scanning.</t>

        <t>All header, control, and framing information, except for network
        addressing information and a session identifier, is encrypted
        according to the Cryptography Profile.</t>

        <t>There is a single session and associated congestion control state
        between a pair of endpoints.</t>

        <t>Each session may have zero or more unidirectional message-oriented
        flows in each direction.  All of a session's sending flows share the
        session's congestion control state.</t>

        <t><xref target="option_association">Return Flow Association</xref>
        generalizes bidirectional communication to arbitrarily complex trees
        of flows.</t>

        <t>Messages in flows can be arbitrarily large and are fragmented for
        transmission.</t>

        <t>Messages of any size may be sent with full, partial, or no reliability
        (sender's choice). Messages may be delivered to the receiving user
        in original queuing order or network arrival order (receiver's
        choice).</t>

        <t><xref target="option_metadata">Flows are named with arbitrary,
        user-defined metadata</xref> rather than port or stream numbers.</t>

        <t>The sequence numbers of each flow are independent of all other
        flows and are not permanently bound to a session-wide transmission
        ordering. This allows real-time priority decisions to be made at
        transmission or retransmission time.</t>

        <t>Each flow has its own receive window and, therefore, independent
        flow control.</t>

        <t>Round trips are expensive and are minimized or eliminated when
        possible.</t>

        <t>After a session is established, flows begin by sending the flow's
        messages with no additional handshake (and associated round trips).</t>

        <t>Transmitting bytes on the network is much more expensive than
        moving bytes in a CPU or memory. Wasted bytes are minimized or
        eliminated when possible and practical, and variable length encodings
        are used, even at the expense of breaking 32-bit alignment and making
        the text diagrams in this specification look awkward.</t>

        <t>P2P lookup and peer introduction (including UDP hole-punching for
        NAT and firewall traversal) are supported directly by the session
        startup handshake.</t>

        <t>Session identifiers allow an endpoint to multiplex many sessions
        over a single local transport address while allowing sessions to
        survive changes in transport address (as may happen in mobile or
        wireless deployments).</t>

</list>
</t>

<t>The syntax of the protocol is detailed in <xref target="syntax"/>.  The
operation of the protocol is detailed in <xref target="operation"/>.</t>

</section> <!-- Goals of RTMFP -->

<section title="Terminology">

<t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL"
in this document are to be interpreted as described in <xref
target="RFC2119"/>.</t>

</section>

</section>

<section anchor="syntax" title="Syntax">

<t>Definitions of types and structures in this specification use traditional
text diagrams paired with procedural descriptions using a C-like syntax. The
C-like procedural descriptions SHALL be construed as definitive.</t>

<t>Structures are packed to take only as many bytes as explicitly indicated.
There is no 32-bit alignment constraint, and fields are not padded for alignment
unless explicitly indicated or described. Text diagrams may include a bit
ruler across the top; this is a convenience for counting bits in individual
fields and does not necessarily imply field alignment on a multiple of the
ruler width.</t>

<t>Unless specified otherwise, reserved fields SHOULD be set to 0 by a sender
and MUST be ignored by a receiver.</t>

<t>The procedural syntax of this specification defines correct and error-free
encoded inputs to a parser. The procedural syntax does not describe a fully
featured parser, including error detection and handling. Implementations MUST
include means to identify error circumstances, including truncations causing
elementary or composed types to not fit inside containing structures, fields,
or elements.  Unless specified otherwise, an error circumstance SHALL abort
the parsing and processing of an element and its enclosing elements, up to
the containing packet.</t>

<section title="Common Elements">

<t>This section lists types and structures that are used throughout this specification.</t>

<section title="Elementary Types and Constructs">

<t>This section lists the elementary types and constructs out of which all
of the following sections' definitions are built.</t>

<t>uint8_t var;</t>
<t><list style="empty">
<t>An unsigned integer 8 bits (one byte) in length and byte aligned.</t>
</list></t>

<t>uint16_t var;</t>
<t><list style="empty">
<t>An unsigned integer 16 bits in length, in network byte order ("big endian")
and byte aligned.</t>
</list></t>

<t>uint32_t var;</t>
<t><list style="empty">
<t>An unsigned integer 32 bits in length, in network byte order and byte
aligned.</t>
</list></t>

<t>uint128_t var;</t>
<t><list style="empty">
<t>An unsigned integer 128 bits in length, in network byte order and byte
aligned.</t>
</list></t>

<t>uintn_t var :bitsize;</t>
<t><list style="empty">
<t>An unsigned integer of any other size, potentially not byte aligned.
Its size in bits is specified explicitly by bitsize.</t>
</list></t>

<t>bool_t var :1;</t>
<t><list style="empty">
<t>A boolean flag having the value true (1 or set) or false (0 or
clear) and being one bit in length.</t>
</list></t>

<t>type var[num];</t>
<t><list style="empty">
<t>A packed array of type with length num*sizeof(type)*8 bits.</t>
</list></t>

<t>struct name_t { ... } name :bitsize;</t>
<t><list style="empty">
<t>A packed structure.  Its size in bits is specified by bitsize.</t>
</list></t>

<t>remainder();</t>
<t><list style="empty">
<t>The number of bytes from the current offset to the end of the enclosing
structure.</t>
</list></t>

<t>type var[remainder()];</t>
<t><list style="empty">
<t>A packed array of type, its size extending to the end of the enclosing
structure.</t>
</list></t>

<t>Note that a bitsize of "variable" indicates that the size of the structure
is determined by the sizes of its interior components.  A bitsize of "n*8"
indicates that the size of the structure is a whole number of bytes and is
byte aligned.</t>

</section> <!-- Elementary Types -->

<section title="Variable Length Unsigned Integer (VLU)">

<t>A VLU encodes any finite non-negative integer into one or more bytes. For
each encoded byte, if the high bit is set, the next byte is also part of the
VLU. If the high bit is clear, this is the final byte of the VLU.  The remaining
bits encode the number, seven bits at a time, from most significant to least
significant.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7                 0 1 2 3 4 5 6 7
+~+~+~+~+~+~+~+~+               +-+-+-+-+-+-+-+-+
|1|    digit    |...............|0|    digit    |
+~+~+~+~+~+~+~+~+               +-+-+-+-+-+-+-+-+
^                               ^
+--------- zero or more --------+

struct vlu_t
{
    value = 0;
    do {
        bool_t  more  :1;
        uintn_t digit :7;
        value = (value * 128) + digit;
    } while(more);
} :variable*8;
]]></artwork></figure>

<figure align="center" title="VLU Depiction in Following Diagrams" anchor="fig.vlu"> <artwork><![CDATA[
+-------------/-+
|             \ |
+-------------/-+
]]></artwork></figure>

<t>Unless stated otherwise in this specification, implementations SHOULD
handle VLUs encoding unsigned integers at least 64 bits in length (that is,
encoding a maximum value of at least 2^64 - 1).</t>

</section> <!-- VLU -->

<section title="Option">

<t>An Option is a Length-Type-Value triplet.  Length and Type are encoded in
VLU format.  Length is the number of bytes of payload following the Length
field.  The payload comprises the Type and Value fields.  Type identifies the
kind of option this is.  The syntax of the Value field is determined by the
type of option.</t>

<t>An Option can have a length of zero, in which case it has no type and no
value and is empty.  An empty Option is called a "Marker".</t>

<figure>
<artwork><![CDATA[
+-------------/-+~~~~~~~~~~~~~/~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|   length    \ |    type     \ |            value              |
+-------------/-+~~~~~~~~~~~~~/~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
                ^                                               ^
                +-------- length bytes long (may be 0) ---------+

struct option_t
{
    vlu_t length :variable*8; // "L"
    if(length > 0)
    {
        struct {
            vlu_t   type :variable*8;   // "T"
            uint8_t value[remainder()]; // "V"
        } payload :length*8;
    }
} :variable*8;
]]></artwork>
</figure>

<figure align="center" title="Option Depiction in Following Diagrams" anchor="fig.option">
<artwork><![CDATA[
+---/---/-------+
| L \ T \   V   |
+---/---/-------+
]]></artwork>
</figure>

</section> <!-- Option -->

<section title="Option List">

<t>An Option List is a sequence of zero or more non-empty Options terminated
by a Marker.</t>

<figure>
<artwork><![CDATA[
+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+-------------/-+
| L \ T \   V   |...............| L \ T \   V   |       0     \ |
+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+-------------/-+
^                                               ^     Marker
+------- zero or more non-empty Options --------+ (empty Option)

struct optionList_t
{
    do
    {
        option_t option :variable*8;
    } while(option.length > 0);
} :variable*8;
]]></artwork>
</figure>

</section> <!-- Option List -->

<section title="Internet Socket Address (Address)" anchor="address">

<t>When communicating an Internet socket address (a combination of
a 32-bit
<xref target="RFC0791">IPv4</xref>
or 128-bit
<xref target="RFC2460">IPv6</xref>
address and a 16-bit port number)
to another RTMFP, this encoding is used.  This encoding additionally
allows an address to be tagged with an origin type, which an RTMFP
MAY use to modify the use or disposition of the address.</t>

<figure>
<artwork><![CDATA[
                                                     1          
 0 1 2 3 4 5 6 7                 0 1 2 3 4 5 6 7|8 9 0 1 2 3 4 5 
+-+-+-+-+-+-+-+-+-----/.../-----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|I|         | O |    Internet   |                               |
|P|0 0 0 0 0| R |    address    |              port             |
|6|   rsv   | I |32 or 128 bits |                               |
+-+-+-+-+-+-+-+-+-----/.../-----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

struct address_t
{
    bool_t  inet6    :1;     // "IP6"
    uintn_t reserved :5 = 0; // "rsv"
    uintn_t origin   :2;     // "ORI"
    if(inet6)
        uint128_t ipAddress;
    else
        uint32_t ipAddress;
    uint16_t port;
} :variable*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="inet6:">If set, the Internet address is a 128-bit IPv6
address. If clear, the Internet address is a 32-bit IPv4 address.</t>

   <t hangText="origin:">The origin tag of this address.  Possible values are:
                <list style="hanging">
                        <t hangText="0:">Unknown, unspecified, or "other"</t>
                        <t hangText="1:">Address was reported by the origin as
                        a local, directly attached interface address</t>
                        <t hangText="2:">Address was observed to be the source
                        address from which a packet was received
                        (a "reflexive transport address" in the terminology of
                        <xref target="RFC5389"/>)</t>
                        <t hangText="3:">Address is a relay, proxy, or
                        introducer (a Redirector and/or Forwarder)</t>
                </list>
        </t>

        <t hangText="ipAddress:">The Internet address, in network byte order.</t>

        <t hangText="port:">The 16-bit port number, in network byte order.</t>
</list></t>

</section> <!-- Address -->

</section> <!-- Common Elements -->

<section title="Network Layer">

<section title="Encapsulation" anchor="network_encapsulation">

<t>RTMFP Multiplex packets are usually carried in <xref target="RFC0768">UDP</xref>
datagrams so that they may transit commonly deployed NATs and firewalls, and
so that RTMFP may be implemented on commonly deployed operating systems without
special privileges or permissions.</t>

<t>RTMFP Multiplex packets MAY be carried by any suitable datagram transport
or encapsulation where endpoints are addressed by an Internet socket address
(that is, an IPv4 or IPv6 address and a 16-bit port number).</t>

<t>The choice of port numbers is not mandated by this specification.  Higher
protocol layers or the application define the port numbers used.</t>

</section> <!-- Encapsulation -->

<section title="Multiplex" anchor="session_multiplex">

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                 Scrambled Session ID (SSID)                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             e             first32[0]                          |
|- - - - - -  n  - - - - - - - - - - - - - - - - - - - - - - - -|
|             c             first32[1]                          |
+- - - - - -  r  - - - - - - - - - - - - - - - - - - - - - - - -+
|             y                                                 |
|             pted packet                                       |
+---------------------------------------------------------------/

struct multiplex_t
{
    uint32_t scrambledSessionID; // "SSID"
    union {
        uint32_t first32[2]; // see note
        uint8_t  encryptedPacket[remainder()];
    } :(encapsulation.length - 4)*8;

    // if encryptedPacket is less than 8 bytes long, treat it
    // as if it were end-padded with 0s for the following:
    sessionID = scrambledSessionID XOR first32[0] XOR first32[1];
} :encapsulation.length*8;
]]></artwork>
</figure>

<t>The 32-bit Scrambled Session ID is the 32-bit session ID modified by
performing a bitwise exclusive-or with the bitwise exclusive-or of the first
two 32-bit words of the encrypted packet.</t>

<t>The session ID is a 32-bit value that the receiver has requested to be used
by the sender when sending packets to this receiver (Sections&nbsp;<xref target="chunk_iikeying" format="counter"/> and 
<xref target="chunk_rikeying" format="counter"/>). The session ID identifies the session to
which this packet belongs and the decryption key to be used to decrypt the
encrypted packet.</t>

<t>Note: Session ID 0 (prior to scrambling) denotes the startup pseudo-session
and implies the Default Session Key.</t>

<t>Note: If the encrypted packet is less than 8 bytes long, then for the
scrambling operation, perform the exclusive-or as though the encrypted packet
were end-padded with enough 0-bytes to bring its length to 8.</t>

</section> <!-- Multiplex -->

<section title="Encryption" anchor="packet_encryption">

<t>RTMFP packets are encrypted according to a Cryptography Profile.  This
specification doesn't define a Cryptography Profile or mandate a particular
choice of cryptography.  The application defines the cryptographic syntax and
algorithms.</t>

<t>Packet encryption is RECOMMENDED to be a block cipher operating in
<xref target="CBC">Cipher Block Chaining</xref> or similar mode.  Encrypted
packets MUST be decipherable without inter-packet dependency, since packets
may be lost, duplicated, or reordered in the network.</t>

<t>The packet encryption layer is responsible for data integrity and authenticity
of packets, for example by means of a checksum or cryptographic message
authentication code. To mitigate replay attacks, data integrity SHOULD comprise
duplicate packet detection, for example by means of a session-wide packet
sequence number.  The packet encryption layer SHALL discard a received packet
that does not pass integrity or authenticity tests.</t>

<t>Note that the structures described below are of plain, unencrypted packets.
Encrypted packets MUST be decrypted according to the Session Key associated
with the Multiplex Session ID before being interpreted according to this
specification.</t>

<t>The Cryptography Profile defines a well-known Default Session Key that is
used at session startup, during which per-session key(s) are negotiated by
the two endpoints.  A session ID of zero denotes use of the Default Session
Key.  The Default Session Key is also used with non-zero session IDs during
the latter phases of session startup (Sections <xref target="chunk_cookie_change" format="counter"/> and
<xref target="chunk_rikeying" format="counter"/>). See <xref target="security_considerations">Security
Considerations</xref> for more about the Default Session Key.</t>

</section> <!-- Encryption -->

<section title="Packet" anchor="packet">

<t>An (unencrypted, plain) RTMFP packet consists of a variable sized common
header, zero or more chunks, and padding.  Padding can be inserted by the
encryption layer of the sender to meet cipher block size constraints and is
ignored by the receiver.  A sender's encryption layer MAY pad the end of a
packet with bytes with value 0xff such that the resulting packet is a natural
and appropriate size for the cipher.  Alternatively, the Cryptography Profile
MAY define its own framing and padding scheme, if needed, such that decrypted
packets are compatible with the syntax defined in this section.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7 
+-+-+-+-+-+-+-+-+
|T|T| r |T|T| M |
|C|C| s |S|S| O |
| |R| v | |E| D |
+-+-+-+-+-+-+-+-+
+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+
|        if(TS) timestamp       |     if(TSE) timestampEcho     |
+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                             Chunk                             |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
                                :
                                :
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                             Chunk                             |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                            padding                            |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct packet_t
{
    bool_t  timeCritical         :1; // "TC"
    bool_t  timeCriticalReverse  :1; // "TCR"
    uintn_t reserved             :2; // "rsv"
    bool_t  timestampPresent     :1; // "TS"
    bool_t  timestampEchoPresent :1; // "TSE"
    uintn_t mode                 :2; // "MOD"
    if(0 != mode)
    {
        if(timestampPresent)
            uint16_t timestamp;
        if(timestampEchoPresent)
            uint16_t timestampEcho;
        while(remainder() > 2)
        {
            uint8_t  chunkType;
            uint16_t chunkLength;
            if(remainder() < chunkLength)
                break;
            uint8_t  chunkPayload[chunkLength];
        } // chunks
        uint8_t padding[remainder()];
    }
} :plainPacket.length*8;
]]></artwork> </figure>

<t><list style="hanging">
        <t hangText="timeCritical:">Time Critical Forward Notification. If set, indicates
                that this packet contains real-time user data.</t>
        <t hangText="timeCriticalReverse:">Time Critical Reverse Notification. If set,
                indicates that the sender is currently receiving packets on other
                sessions that have the timeCritical flag set.</t>
        <t hangText="timestampPresent:">If set, indicates that the timestamp field is
                present. If clear, there is no timestamp field.</t>
        <t hangText="timestampEchoPresent:">If set, indicates that the timestamp echo
                field is present.  If clear, there is no timestamp echo field.</t>
        <t hangText="mode:">The mode of this packet.  See below for additional discussion of
                packet modes.  Possible values are:
                <list style="hanging">
                        <t hangText="0:">Forbidden value</t>
                        <t hangText="1:">Initiator Mark</t>
                        <t hangText="2:">Responder Mark</t>
                        <t hangText="3:">Startup</t>
                </list></t>
        <t hangText="timestamp:">If the timestampPresent flag is set, this field is
                present and contains the low 16 bits of the sender's 250 Hz clock
                (4 milliseconds per tick) at transmit time.  The sender's clock MAY have its
                origin at any time in the past.</t>
        <t hangText="timestampEcho:">If the timestampEchoPresent flag is set, this field
                is present and contains the sender's estimate of what the timestamp field
                of a packet received from the other end would be at the time this packet
                was transmitted, using the method described in <xref target="compute_ERTO"/>.</t>
        <t hangText="chunks:">Zero or more chunks follow the header.  It is RECOMMENDED that
                a packet contain at least one chunk.</t>
        <t hangText="padding:">Zero or more bytes of padding follow the chunks.  The following
                conditions indicate padding:
                <list style="symbols">
                        <t>Fewer than three bytes (the size of a chunk header) remain in the packet.</t>
                        <t>The chunkLength field of what would be the current chunk header indicates
                that the hypothetical chunk payload wouldn't fit in the remaining bytes of
                the packet.</t>
                </list>
        </t>
</list></t>

<t>Packet mode 0 is not allowed.  Packets marked with this mode are invalid
and MUST be discarded.</t>

<t>The original initiator of a session MUST mark all non-startup packets it
sends in that session with packet mode 1 ("Initiator Mark").  It SHOULD ignore
any packet received in that session with packet mode 1.</t>

<t>The original responder of a session MUST mark all non-startup packets it
sends in that session with packet mode 2 ("Responder Mark").  It SHOULD ignore
any packet received in that session with packet mode 2.</t>

<t>Packet mode 3 is for session startup.  Session startup chunks are only
allowed in packets with this mode.</t>

<t>Chunks that are not for session startup are only allowed in packets with
modes 1 or 2.</t>

</section> <!-- Packet -->

</section> <!-- Packet Elements -->

<section title="Chunks" anchor="chunks">

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   chunkType   |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|        chunkPayload (chunkLength bytes, may be zero)          |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct chunk_t
{
    uint8_t  chunkType;
    uint16_t chunkLength;
    uint8_t  chunkPayload[chunkLength];
} :variable*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="chunkType:">The chunk type code.</t>
        <t hangText="chunkLength:">The size, in bytes, of the chunk payload.</t>
        <t hangText="chunkPayload:">The type-specific payload of this chunk,
                chunkLength bytes in length (may be empty).</t>
</list></t>

<t>Defined chunk types are enumerated here in the order they might be encountered
in the course of a typical session. The following chunk type codes are
defined:<list style="hanging">
        <t hangText="0x7f:">Packet Fragment (<xref target="chunk_packetFragment" />)</t>
        <t hangText="0x30:">Initiator Hello (<xref target="chunk_ihello" />)</t>
        <t hangText="0x0f:">Forwarded Initiator Hello (<xref target="chunk_fihello" />)</t>
        <t hangText="0x70:">Responder Hello (<xref target="chunk_rhello" />)</t>
        <t hangText="0x71:">Responder Redirect (<xref target="chunk_redirect" />)</t>
        <t hangText="0x79:">RHello Cookie Change (<xref target="chunk_cookie_change" />)</t>
        <t hangText="0x38:">Initiator Initial Keying (<xref target="chunk_iikeying" />)</t>
        <t hangText="0x78:">Responder Initial Keying (<xref target="chunk_rikeying" />)</t>
        <t hangText="0x01:">Ping (<xref target="chunk_ping" />)</t>
        <t hangText="0x41:">Ping Reply (<xref target="chunk_ping_reply" />)</t>
        <t hangText="0x10:">User Data (<xref target="chunk_data" />)</t>
        <t hangText="0x11:">Next User Data (<xref target="chunk_next_data" />)</t>
        <t hangText="0x50:">Data Acknowledgement Bitmap (<xref target="chunk_bitmap_ack" />)</t>
        <t hangText="0x51:">Data Acknowledgement Ranges (<xref target="chunk_range_ack" />)</t>
        <t hangText="0x18:">Buffer Probe (<xref target="chunk_buffer_probe" />)</t>
        <t hangText="0x5e:">Flow Exception Report (<xref target="chunk_flow_exception" />)</t>
        <t hangText="0x0c:">Session Close Request (<xref target="chunk_close" />)</t>
        <t hangText="0x4c:">Session Close Acknowledgement (<xref target="chunk_close_ack" />)</t>
        <t hangText="0x00:">Ignore/Padding</t>
        <t hangText="0xff:">Ignore/Padding</t>
</list></t>

<t>A receiver MUST ignore a chunk having an unrecognized chunk type code. A
receiver MUST ignore a chunk appearing in a packet having a mode inappropriate
to that chunk type.</t>

<t>Unless specified otherwise, if a chunk has a syntax or processing error
(for example, the chunk's payload field is not long enough to contain the
specified syntax elements), the chunk SHALL be ignored as though it was not
present in the packet, and parsing and processing SHALL commence with the
next chunk in the packet, if any.</t>

<section title="Packet Fragment Chunk" anchor="chunk_packetFragment">

<t>This chunk is used to divide a <xref target="packet">plain RTMFP packet</xref>
that is unavoidably larger than the path MTU (such as session startup packets
containing <xref target="chunk_rhello">Responder Hello</xref> or
<xref target="chunk_iikeying">Initiator Initial Keying</xref> chunks with
large certificates) into segments that do not exceed the path MTU, and to
allow the segments to be sent through the network at a moderated rate to avoid
jamming interfaces, links, or paths.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x7f     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-------------/-+-------------/-+
|M|  reserved   |   packetID  \ | fragmentNum \ |
+-+-+-+-+-+-+-+-+-------------/-+-------------/-+
+---------------------------------------------------------------+
|                         packetFragment                        |
+---------------------------------------------------------------/

struct fragmentChunkPayload_t
{
    bool_t  moreFragments :1; // M
    uintn_t reserved      :7;
    vlu_t   packetID      :variable*8;
    vlu_t   fragmentNum   :variable*8;
    uint8_t packetFragment[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="moreFragments:">If set, the indicated packet
                comprises additional fragments.  If clear, this fragment is
                the final fragment of the packet.</t>
        <t hangText="reserved:">Reserved for future use.</t>
        <t hangText="packetID:">VLU, the identifier of this segmented packet.
                All fragments of the same packet have the same packetID.</t>
        <t hangText="fragmentNum:">VLU, the index of this fragment of the
                indicated packet. The first fragment of the packet MUST be index
                0. Fragments are numbered consecutively.</t>
        <t hangText="packetFragment:">The bytes of the indicated segment of
                the indicated original plain RTMFP packet. A packetFragment
                MUST NOT be empty.</t>
</list></t>

<t>The use of this mechanism is detailed in
<xref target="fragmentation"/>.</t>

</section> <!-- Packet Fragment -->

<section title="Initiator Hello Chunk (IHello)" anchor="chunk_ihello">

<t>This chunk is sent by the initiator of a new session to begin the startup
handshake.  This chunk is only allowed in a packet with Session ID 0, encrypted
with the Default Session Key, and having packet mode 3 (Startup).</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x30     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-----------------------------------------------+
|  epdLength  \ |    endpointDiscriminator (epdLength bytes)    |
+-------------/-+-----------------------------------------------/
+---------------------------------------------------------------+
|                              tag                              |
+---------------------------------------------------------------/

struct ihelloChunkPayload_t
{
    vlu_t   epdLength :variable*8;
    uint8_t endpointDiscriminator[epdLength];
    uint8_t tag[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="epdLength:">VLU, the length of the following endpointDiscriminator field in bytes.</t>
        <t hangText="endpointDiscriminator:">The Endpoint Discriminator for the identity with which the
                initiator wants to communicate.</t>
        <t hangText="tag:">Initiator-provided data to be returned in a Responder Hello's tagEcho field.
                The tag/tagEcho is used to match Responder Hellos to the initiator's session startup
                state independent of the responder's address.</t>
</list></t>

<t>The use of IHello is detailed in <xref target="session_startup" />.</t>

</section> <!-- IHello -->

<section title="Forwarded Initiator Hello Chunk (FIHello)" anchor="chunk_fihello">

<t>This chunk is sent on behalf of an initiator by a Forwarder.  It is only
allowed in packets of an established session having packet mode 1 or 2.  A
receiver MAY treat this chunk as though it was an Initiator Hello received
directly from replyAddress.  Alternatively, if the receiver is selected by
the Endpoint Discriminator, it MAY respond to replyAddress with an Implied
Redirect (<xref target="chunk_redirect" />).</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x0f     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-----------------------------------------------+
|  epdLength  \ |    endpointDiscriminator (epdLength bytes)    |
+-------------/-+-----------------------------------------------/
+---------------------------------------------------------------+
|                          replyAddress                         |
+---------------------------------------------------------------/
+---------------------------------------------------------------+
|                              tag                              |
+---------------------------------------------------------------/

struct fihelloChunkPayload_t
{
    vlu_t     epdLength :variable*8;
    uint8_t   endpointDiscriminator[epdLength];
    address_t replyAddress :variable*8;
    uint8_t   tag[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="epdLength:">VLU, the length of the following endpointDiscriminator field in bytes.</t>
        <t hangText="endpointDiscriminator:">The Endpoint Discriminator for the identity with which the
                original initiator wants to communicate, copied from the original Initiator Hello.</t>
        <t hangText="replyAddress:">Address format (<xref target="address"/>), the address that the
                forwarding node derived from the received Initiator Hello, to which the receiver
                should respond.</t>
        <t hangText="tag:">Copied from the original Initiator Hello.</t>
</list></t>

<t>The use of FIHello is detailed in <xref target="session_forwarder" />.</t>

</section> <!-- FIHello -->

<section title="Responder Hello Chunk (RHello)" anchor="chunk_rhello">

<t>This chunk is sent by a responder in response to an Initiator Hello or
Forwarded Initiator Hello if the Endpoint Discriminator indicates the responder's
identity.
This chunk is only allowed in a packet with Session ID 0, encrypted
with the Default Session Key, and having packet mode 3 (Startup).</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x70     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-----------------------------------------------+
|  tagLength  \ |            tagEcho (tagLength bytes)          |
+-------------/-+-----------------------------------------------/
+-------------/-+-----------------------------------------------+
| cookieLength\ |           cookie (cookieLength bytes)         |
+-------------/-+-----------------------------------------------/
+---------------------------------------------------------------+
|                     responderCertificate                      |
+---------------------------------------------------------------/

struct rhelloChunkPayload_t
{
    vlu_t   tagLength :variable*8;
    uint8_t tagEcho[tagLength];
    vlu_t   cookieLength :variable*8;
    uint8_t cookie[cookieLength];
    uint8_t responderCertificate[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="tagLength:">VLU, the length of the following tagEcho field in bytes.</t>
        <t hangText="tagEcho:">The tag from the Initiator Hello, unaltered.</t>
        <t hangText="cookieLength:">VLU, the length of the following cookie field in bytes.</t>
        <t hangText="cookie:">Responder-created state data to authenticate a future
                Initiator Initial Keying message (in order to prevent
denial-of-service attacks).</t>
        <t hangText="responderCertificate:">The responder's cryptographic credentials.</t>
</list></t>

<t>Note: This specification doesn't mandate a specific choice of certificate
format.  The Cryptography Profile determines the syntax, algorithms, and
interpretation of the responderCertificate.</t>

<t>The use of RHello is detailed in <xref target="session_startup" />.</t>

</section> <!-- RHello -->

<section title="Responder Redirect Chunk (Redirect)" anchor="chunk_redirect">

<t>This chunk is sent in response to an Initiator Hello or Forwarded
Initiator Hello to indicate that the requested endpoint can be reached
at one or more of the indicated addresses. A receiver can add none,
some, or all of the indicated addresses to the set of addresses to
which it is sending Initiator Hello messages for the opening session
associated with tagEcho.
This chunk is only allowed in a packet with Session ID 0, encrypted
with the Default Session Key, and having packet mode 3 (Startup).</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x71     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-----------------------------------------------+
|  tagLength  \ |            tagEcho (tagLength bytes)          |
+-------------/-+-----------------------------------------------/
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                     redirectDestination 1                     |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
                                :
                                :
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                     redirectDestination N                     |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct responderRedirectChunkPayload_t
{
    vlu_t   tagLength :variable*8;
    uint8_t tagEcho[tagLength];
    addressCount = 0;
    while(remainder() > 0)
    {
        address_t redirectDestination :variable*8;
        addressCount++;
    }
    if(0 == addressCount)
        redirectDestination = packetSourceAddress();
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="tagLength:">VLU, the length of the following tagEcho field in bytes.</t>
        <t hangText="tagEcho:">The tag from the Initiator Hello, unaltered.</t>
        <t hangText="redirectDestination:">(Zero or more) Address format (<xref
target="address"/>) addresses to add to the opening set for the indicated
session.</t>
</list></t>

<t>If this chunk lists zero redirectDestination addresses, then this is an
Implied Redirect, and the indicated address is the address from which the
packet containing this chunk was received.</t>

<t>The use of Redirect is detailed in Sections <xref target="initiator_startup"
format="counter"/>, <xref target="responder_startup" format="counter" />, and <xref target="session_redirector" format="counter"/>.</t>

</section> <!-- Redirect -->

<section title="RHello Cookie Change Chunk" anchor="chunk_cookie_change">

<t>This chunk SHOULD be sent by a responder to an initiator in response to an
Initiator Initial Keying if that chunk's cookie appears to have been created
by the responder but the cookie is incorrect (for example, it includes a hash
of the initiator's address, but the initiator's address is different than the
one that elicited the Responder Hello containing the original cookie).</t>

<t>This chunk is only allowed in a packet encrypted with the Default Session
Key and having packet mode 3, and with the session ID indicated in the
initiatorSessionID field of the Initiator Initial Keying to which this is a
response.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x79     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-----------------------------------------------+
| oldCookieLen\ |        oldCookie (oldCookieLen bytes)         |
+-------------/-+-----------------------------------------------/
+---------------------------------------------------------------+
|                           newCookie                           |
+---------------------------------------------------------------/

struct rhelloCookieChangeChunkPayload_t
{
    vlu_t   oldCookieLen :variable*8;
    uint8_t oldCookie[oldCookieLen];
    uint8_t newCookie[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="oldCookieLen:">VLU, the length of the following oldCookie field in bytes.</t>
        <t hangText="oldCookie:">The cookie that was sent in a previous Responder Hello and Initiator
                Initial Keying.</t>
        <t hangText="newCookie:">The new cookie that the responder would like sent (and signed)
                in a replacement Initiator Initial Keying. The old and new cookies need not
                have the same lengths.</t>
</list></t>

<t>On receipt of this chunk, the initiator SHOULD compute, sign, and send a
new Initiator Initial Keying having newCookie in place of oldCookie. The use
of this chunk is detailed in <xref target="session_cookie_change" />.</t>

</section> <!-- RHello Cookie Change -->

<section title="Initiator Initial Keying Chunk (IIKeying)" anchor="chunk_iikeying">

<t>This chunk is sent by an initiator to establish a session with a responder.
The initiator MUST have obtained a valid cookie to use with the responder,
typically by receiving a Responder Hello from it.
This chunk is only allowed in a packet with Session ID 0, encrypted
with the Default Session Key, and having packet mode 3 (Startup).</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x38     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       initiatorSessionID                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-----------------------------------------------+
| cookieLength\ |                  cookieEcho                   |
+-------------/-+-----------------------------------------------/
+-------------/-+-----------------------------------------------+
|  certLength \ |             initiatorCertificate              |
+-------------/-+-----------------------------------------------/
+-------------/-+-----------------------------------------------+
|  skicLength \ |          sessionKeyInitiatorComponent         |
+-------------/-+-----------------------------------------------/
+---------------------------------------------------------------+
|                           signature                           |
+---------------------------------------------------------------/

struct iikeyingChunkPayload_t
{
    struct
    {
        uint32_t initiatorSessionID;
        vlu_t    cookieLength :variable*8;
        uint8_t  cookieEcho[cookieLength];
        vlu_t    certLength :variable*8;
        uint8_t  initiatorCertificate[certLength];
        vlu_t    skicLength :variable*8;
        uint8_t  sessionKeyInitiatorComponent[skicLength];
    } initiatorSignedParameters :variable*8;
    uint8_t signature[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="initiatorSessionID:">The session ID to be used by the responder
                when sending packets to the initiator.</t>
        <t hangText="cookieLength:">VLU, the length of the following cookieEcho field in bytes.</t>
        <t hangText="cookieEcho:">The cookie from the Responder Hello, unaltered.</t>
        <t hangText="certLength:">VLU, the length of the following initiatorCertificate
                field in bytes.</t>
        <t hangText="initiatorCertificate:">The initiator's identity credentials.</t>
        <t hangText="skicLength:">VLU, the length of the following sessionKeyInitiatorComponent
                field in bytes.</t>
        <t hangText="sessionKeyInitiatorComponent:">The initiator's portion of the session key negotiation
                according to the Cryptography Profile.</t>
        <t hangText="initiatorSignedParameters:">The payload portion of this chunk up to
                the signature field.</t>
        <t hangText="signature:">The initiator's digital signature of the initiatorSignedParameters
                according to the Cryptography Profile.</t>
</list></t>

<t>Note: This specification doesn't mandate a specific choice of cryptography.
The Cryptography Profile determines the syntax, algorithms, and interpretation
of the initiatorCertificate, responderCertificate, sessionKeyInitiatorComponent,
sessionKeyResponderComponent, and signature, and how the sessionKeyInitiatorComponent
and sessionKeyResponderComponent are combined to derive the session keys.</t>

<t>The use of IIKeying is detailed in <xref target="session_startup" />.</t>

</section> <!-- IIKeying -->

<section title="Responder Initial Keying Chunk (RIKeying)" anchor="chunk_rikeying">

<t>This chunk is sent by a responder in response to an Initiator Initial Keying as the
final phase of session startup.
This chunk is only allowed in a packet encrypted with the Default Session Key,
having packet mode 3 (Startup), and sent to the initiator with the session
ID specified by the initiatorSessionID field from the Initiator Initial
Keying.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x78     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       responderSessionID                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-----------------------------------------------+
|  skrcLength \ |         sessionKeyResponderComponent          |
+-------------/-+-----------------------------------------------/
+---------------------------------------------------------------+
|                           signature                           |
+---------------------------------------------------------------/

struct rikeyingChunkPayload_t
{
    struct
    {
        uint32_t responderSessionID;
        vlu_t    skrcLength :variable*8;
        uint8_t  sessionKeyResponderComponent[skrcLength];
    } responderSignedParametersPortion :variable*8;
    uint8_t  signature[remainder()];
} :chunkLength*8;

struct
{
    responderSignedParametersPortion;
    sessionKeyInitiatorComponent;
} responderSignedParameters;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="responderSessionID:">The session ID to be used
                by the initiator when sending packets to the responder.</t>
        <t hangText="skrcLength:">VLU, the length of the following
                sessionKeyResponderComponent field in bytes.</t>
        <t hangText="sessionKeyResponderComponent:">The responder's portion
                of the session key negotiation according to the Cryptography
                Profile.</t>
        <t hangText="responderSignedParametersPortion:">The payload
                portion of this chunk up to the signature field.</t>
        <t hangText="signature:">The responder's digital signature of the
                responderSignedParameters (see below) according to the
                Cryptography Profile.</t>
        <t hangText="responderSignedParameters:">The concatenation of the
                responderSignedParametersPortion (the payload portion of
                this chunk up to the signature field) and the
                sessionKeyInitiatorComponent from the Initiator Initial Keying
                to which this chunk is a response.</t>
</list></t>

<t>Note: This specification doesn't mandate a specific choice of cryptography.
The Cryptography Profile determines the syntax, algorithms, and interpretation
of the initiatorCertificate, responderCertificate, sessionKeyInitiatorComponent,
sessionKeyResponderComponent, and signature, and how the sessionKeyInitiatorComponent
and sessionKeyResponderComponent are combined to derive the session keys.</t>

<t>Once the responder has computed the sessionKeyResponderComponent, it has
all of the information and state necessary for an established session with
the initiator.  Once the responder has sent this chunk to the initiator, the
session is established and ready to carry flows of user data.</t>

<t>Once the initiator receives, verifies, and processes this chunk, it has
all of the information and state necessary for an established session with
the responder.  The session is established and ready to carry flows of user data.</t>

<t>The use of RIKeying is detailed in <xref target="session_startup" />.</t>

</section> <!-- RIKeying -->

<section title="Ping Chunk" anchor="chunk_ping">

<t>This chunk is sent in order to elicit a Ping Reply from the receiver.
It is only allowed in a packet belonging to an established session and
having packet mode 1 or 2.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x01     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                             message                           |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct pingChunkPayload_t
{
    uint8_t message[chunkLength];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="message:">The (potentially empty) message that is expected
                to be returned by the other end of the session in a Ping Reply.</t>
</list></t>

<t>The receiver of this chunk SHOULD reply as immediately as is practical
with a Ping Reply.</t>

<t>Ping and the expected Ping Reply are typically used for session keepalive,
endpoint address change verification, and path MTU discovery. See
<xref target="ping" /> for details.</t>

</section> <!-- Ping -->

<section title="Ping Reply Chunk" anchor="chunk_ping_reply">

<t>This chunk is sent in response to a Ping chunk.
It is only allowed in a packet belonging to an established session and
having packet mode 1 or 2.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x41     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                           messageEcho                         |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct pingReplyChunkPayload_t
{
    uint8_t messageEcho[chunkLength];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="messageEcho:">The message from the Ping to which this is a response, unaltered.</t>
</list></t>

</section> <!-- Ping Reply -->

<section title="User Data Chunk" anchor="chunk_data">

<t>This chunk is the basic unit of transmission for the user messages of a
flow.  A user message comprises one or more fragments.  Each fragment is
carried in its own chunk and has a unique sequence number in its flow.  It
is only allowed in a packet belonging to an established session and having packet
mode 1 or 2.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x10     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+
|O|r| F | r |A|F|
|P|s| R | s |B|I|
|T|v| A | v |N|N|
+-+-+-+-+-+-+-+-+
+-------------/-+-------------/-+-------------/-+
|   flowID    \ |     seq#    \ |  fsnOffset  \ |
+-------------/-+-------------/-+-------------/-+
+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+-------------/-+
| L \ T \   V   |... options ...| L \ T \   V   |       0     \ |
\~~~/~~~/~~~~~~~+   [if(OPT)]   +~~~/~~~/~~~~~~~+-------------/-/
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                            userData                           |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct userDataChunkPayload_t
{
    bool_t  optionsPresent :1;  // "OPT"
    uintn_t reserved1 :1;       // "rsv"
    uintn_t fragmentControl :2; // "FRA"
        // 0=whole, 1=begin, 2=end, 3=middle
    uintn_t reserved2 :2;       // "rsv"
    bool_t  abandon :1;         // "ABN"
    bool_t  final :1;           // "FIN"
    vlu_t   flowID :variable*8;
    vlu_t   sequenceNumber :variable*8; // "seq#"
    vlu_t   fsnOffset :variable*8;
    forwardSequenceNumber = sequenceNumber - fsnOffset;
    if(optionsPresent)
        optionList_t options :variable*8;
    uint8_t userData[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="optionsPresent:">If set, indicates the presence of an option list
                before the user data. If clear, there is no option list in this chunk.</t>
        <t hangText="fragmentControl:">Indicates how this fragment is assembled, potentially with
                others, into a complete user message.  Possible values:
                <list style="hanging">
                        <t hangText="0:">This fragment is a complete message.</t>
                        <t hangText="1:">This fragment is the first of a multi-fragment message.</t>
                        <t hangText="2:">This fragment is the last of a multi-fragment message.</t>
                        <t hangText="3:">This fragment is in the middle of a multi-fragment message.</t>
                </list>
                A single-fragment user message has a fragment control of "0-whole".  When a message
                has more than one fragment, the first fragment has a fragment control of "1-begin", then zero or
                more "3-middle" fragments, and finally a "2-end" fragment. The sequence numbers of a multi-fragment
                message MUST be contiguous.
        </t>
        <t hangText="abandon:">If set, this sequence number has been abandoned by the sender.
                The userData, if any, MUST be ignored.</t>
        <t hangText="final:">If set, this is the last sequence number of the flow.</t>
        <t hangText="flowID:">VLU, the flow identifier.</t>
        <t hangText="sequenceNumber:">VLU, the sequence number of this fragment.  Fragments
                are assigned contiguous increasing sequence numbers in a flow. The first
                sequence number of a flow SHOULD be 1.  The first sequence
                number of a flow MUST be greater than zero. Sequence numbers
                are unbounded and do not wrap.</t>
        <t hangText="fsnOffset:">VLU, the difference between the sequence number
                and the Forward Sequence Number. This field MUST NOT be
                zero if the abandon flag is not set. This field MUST NOT be
                greater than sequenceNumber.</t>
        <t hangText="forwardSequenceNumber:">The flow sender will not send (or resend) any
                fragment with a sequence number less than or equal to the Forward Sequence Number.</t>
        <t hangText="options:">If the optionsPresent flag is set, a list of zero or more Options
                terminated by a Marker is present.  See <xref target="data_options"/> for defined options.</t>
        <t hangText="userData:">The actual user data for this fragment.</t>
</list></t>

<t>The use of User Data is detailed in <xref target="flow_sender"/>.</t>

<section title="Options for User Data" anchor="data_options">

<t>This section lists options that may appear in User Data option lists.  A
conforming implementation MUST support the options in this section.</t>

<t>A flow receiver MUST reject a flow containing a flow option that is not
understood if the option type is less than 8192 (0x2000).  A flow receiver
MUST ignore any flow option that is not understood if the option type is 8192
or greater.</t>

<t>The following option type codes are defined for User Data:<list style="hanging">
        <t hangText="0x00:">User's Per-Flow Metadata (<xref target="option_metadata" />)</t>
        <t hangText="0x0a:">Return Flow Association (<xref target="option_association" />)</t>
</list>
</t>

<section anchor="option_metadata" title="User's Per-Flow Metadata">

<t>This option conveys the user's per-flow metadata for the flow to which it's attached.</t>

<figure>
<artwork><![CDATA[
+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|   length    \ |     0x00    \ |         userMetadata          |
+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct userMetadataOptionValue_t
{
    uint8_t userMetadata[remainder()];
} :remainder()*8;
]]></artwork>
</figure>

<t>The user associates application-defined metadata with each flow. The
metadata does not change over the life of the flow.  Every flow MUST have
metadata.  A flow sender MUST send this option with the first User Data chunk
for this flow in each packet until an acknowledgement for this flow is received.
A flow sender SHOULD NOT send this option more than once for each flow in any
one packet.  A flow sender SHOULD NOT send this option for a flow once the
flow has been acknowledged.</t>

<t>This specification doesn't mandate the encoding, syntax, or interpretation
of the user's per-flow metadata; this is determined by the application.</t>

<t>The userMetadata SHOULD NOT exceed 512 bytes. The userMetadata MAY be 0
bytes in length.</t>

</section> <!-- User Metadata -->

<section anchor="option_association" title="Return Flow Association">

<t>A new flow can be considered to be in return (or response) to a flow sent
by the other endpoint.  This option encodes the receive flow identifier to
which this new sending flow is a response.</t>

<figure>
<artwork><![CDATA[
+-------------/-+-------------/-+-------------/-+
|   length    \ |     0x0a    \ |    flowID   \ |
+-------------/-+-------------/-+-------------/-+

struct returnFlowAssociationOptionValue_t
{
    vlu_t flowID :variable*8;
} :variable*8;
]]></artwork>
</figure>

<t>Consider endpoints A and B.  Endpoint A begins a flow with identifier&nbsp;5
to endpoint B.  A is the flow sender for A's flowID=5, and B is the flow
receiver for A's flowID=5.  B begins a return flow with identifier 7 to A in
response to A's flowID=5.  B is the flow sender for B's flowID=7, and A is the
flow receiver for B's flowID=7.  B sends this option with flowID set to 5 to
indicate that B's flowID=7 is in response to and associated with A's flowID=5.</t>

<t>If there is a return association, the flow sender MUST send this option
with the first User Data chunk for this flow in each packet until an acknowledgement
for this flow is received.  A flow sender SHOULD NOT send this option more
than once for each flow in any one packet.  A flow sender SHOULD NOT send
this option for a flow once the flow has been acknowledged.</t>

<t>A flow MUST NOT indicate more than one return association.</t>

<t>A flow MUST indicate its return association, if any, upon its first
transmission of a User Data chunk. A return association can't be added to a
sending flow after it begins.</t>

<t>A flow receiver MUST reject a new receiving flow having a return flow
association that does not indicate an F_OPEN sending flow.</t>

</section> <!-- Return Flow Association -->

</section> <!-- User Data Options -->

</section> <!-- User Data -->

<section title="Next User Data Chunk" anchor="chunk_next_data">

<t>This chunk is equivalent to the User Data chunk for purposes of sending
the user messages of a flow.  When used, it MUST follow a User Data chunk
or another Next User Data chunk in the same packet.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x11     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+
|O|r| F | r |A|F|
|P|s| R | s |B|I|
|T|v| A | v |N|N|
+-+-+-+-+-+-+-+-+
+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+-------------/-+
| L \ T \   V   |... options ...| L \ T \   V   |       0     \ |
\~~~/~~~/~~~~~~~+   [if(OPT)]   +~~~/~~~/~~~~~~~+-------------/-/
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                            userData                           |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct nextUserDataChunkPayload_t
{
    bool_t  optionsPresent :1;  // "OPT"
    uintn_t reserved1 :1;       // "rsv"
    uintn_t fragmentControl :2; // "FRA"
        // 0=whole, 1=begin, 2=end, 3=middle
    uintn_t reserved2 :2;       // "rsv"
    bool_t  abandon :1;         // "ABN"
    bool_t  final :1;           // "FIN"
    if(optionsPresent)
        optionList_t options :variable*8;
    uint8_t userData[remainder()];
} :chunkLength*8;
]]></artwork>
</figure>

<t>This chunk is considered to be for the same flowID as the most recently
preceding User Data or Next User Data chunk in the same packet, having the
same Forward Sequence Number, and having the next sequence number.  The
optionsPresent, fragmentControl, abandon, and final flags, and the options
(if present), have the same interpretation as for the User Data chunk.</t>

<figure align="center" title="Sequential Messages in One Packet Using Next User Data" anchor="fig.fragmented_message">
<artwork><![CDATA[
...
----------+------------------------------------
10 00 07  | User Data chunk, length=7
00        | OPT=0, FRA=0 "whole", ABN=0, FIN=0
02 05 03  | flowID=2, seq#=5, fsn=(5-3)=2
00 01 02  | data 3 bytes: 00, 01, 02
----------+------------------------------------
11 00 04  | Next User Data chunk,length=4
00        | OPT=0, FRA=0 "whole", ABN=0, FIN=0
          | flowID=2, seq#=6, fsn=2
03 04 05  | data 3 bytes: 03, 04, 05
----------+------------------------------------
11 00 04  | Next User Data chunk, length=4
00        | OPT=0, FRA=0 "whole", ABN=0, FIN=0
          | flowID=2, seq#=7, fsn=2
06 07 08  | data 3 bytes: 06, 07, 08
----------+------------------------------------
]]></artwork>
<postamble>
</postamble>
</figure>

<t>The use of Next User Data is detailed in <xref target="send_next_data"/>.</t>

</section> <!-- Next User Data -->

<section title="Data Acknowledgement Bitmap Chunk (Bitmap Ack)" anchor="chunk_bitmap_ack">

<t>This chunk is sent by the flow receiver to indicate to the flow sender the
User Data fragment sequence numbers that have been received for one flow.  It
is only allowed in a packet belonging to an established session and having packet
mode 1 or 2.</t>

<t>The flow receiver can choose to acknowledge User Data with this chunk or
with a Range Ack.  It SHOULD choose whichever format has the most compact
encoding of the sequence numbers received.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x50     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-------------/-+-------------/-+
|   flowID    \ |   bufAvail  \ |    cumAck   \ |
+-------------/-+-------------/-+-------------/-+
+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+
|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|C|
|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|+|
|9|8|7|6|5|4|3|2|1|1|1|1|1|1|1|1|2|2|2|2|2|2|1|1| ....
| | | | | | | | |7|6|5|4|3|2|1|0|5|4|3|2|1|0|9|8|
+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+

struct dataAckBitmapChunkPayload_t
{
    vlu_t flowID :variable*8;
    vlu_t bufferBlocksAvailable :variable*8; // "bufAvail"
    vlu_t cumulativeAck :variable*8; // "cumAck"
    bufferBytesAvailable = bufferBlocksAvailable * 1024;
    acknowledge(0 through cumulativeAck);
    ackCursor = cumulativeAck + 1;
    while(remainder() > 0)
    {
        for(bitPosition = 8; bitPosition > 0; bitPosition--)
        {
            bool_t bit :1;
            if(bit)
                acknowledge(ackCursor + bitPosition);
        }
        ackCursor += 8;
    }
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="flowID:">VLU, the flow identifier.</t>
        <t hangText="bufferBlocksAvailable:">VLU, the number of 1024-byte blocks of User Data
                that the receiver is currently able to accept. <xref target="receive_flow_control"/>
                describes how to calculate this value.</t>
        <t hangText="cumulativeAck:">VLU, the acknowledgement of every fragment sequence
                number in this flow that is less than or equal to this value.
                This MUST NOT be less than the highest Forward Sequence Number
                received in this flow.</t>
        <t hangText="bit field:">A sequence of zero or more bytes representing a bit
                field of received fragment sequence numbers after the cumulative acknowledgement,
                least significant bit first.  A set bit
                indicates receipt of a sequence number.  A clear bit indicates that sequence
                number was not received.  The least significant bit of the first byte is
                the second sequence number following the cumulative
acknowledgement, the next bit is the third sequence number following, and so
on.</t>

<t><xref target="fig.bitmap_ack"/> shows an example Bitmap Ack indicating
acknowledgement of fragment sequence numbers 0 through 16, 18, 21 through 24,
27, and&nbsp;28.</t>
</list></t>

<figure align="center" anchor="fig.bitmap_ack" title="Example Bitmap Ack">
<artwork><![CDATA[
50 00 05  | Bitmap Ack, length=5 bytes
05 7f 10  | flowID=5, bufAvail=127*1024 bytes, cumAck=0..16
79 06     | 01111001 00000110 = 18, 21, 22, 23, 24, 27, 28
]]></artwork>
</figure>

</section> <!-- Bitmap Ack -->

<section title="Data Acknowledgement Ranges Chunk (Range Ack)" anchor="chunk_range_ack">

<t>This chunk is sent by the flow receiver to indicate to the flow sender the
User Data fragment sequence numbers that have been received for one flow.  It
is only allowed in a packet belonging to an established session and having packet
mode 1 or 2.</t>

<t>The flow receiver can choose to acknowledge User Data with this chunk or
with a Bitmap Ack.  It SHOULD choose whichever format has the most compact
encoding of the sequence numbers received.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x51     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-------------/-+-------------/-+
|   flowID    \ |   bufAvail  \ |    cumAck   \ |
+-------------/-+-------------/-+-------------/-+
+~~~~~~~~~~~~~/~+~~~~~~~~~~~~~/~+
|   #holes-1  \ |   #recv-1   \ |
+~~~~~~~~~~~~~/~+~~~~~~~~~~~~~/~+
                :
                :
+~~~~~~~~~~~~~/~+~~~~~~~~~~~~~/~+
|   #holes-1  \ |   #recv-1   \ |
+~~~~~~~~~~~~~/~+~~~~~~~~~~~~~/~+

struct dataAckRangesChunkPayload_t
{
    vlu_t flowID :variable*8;
    vlu_t bufferBlocksAvailable :variable*8; // "bufAvail"
    vlu_t cumulativeAck :variable*8; // "cumAck"
    bufferBytesAvailable = bufferBlocksAvailable * 1024;
    acknowledge(0 through cumulativeAck);
    ackCursor = cumulativeAck;
    while(remainder() > 0)
    {
        vlu_t holesMinusOne :variable*8; // "#holes-1"
        vlu_t receivedMinusOne :variable*8; // "#recv-1"

        ackCursor++;
        rangeFrom = ackCursor + holesMinusOne + 1;
        rangeTo = rangeFrom + receivedMinusOne;
        acknowledge(rangeFrom through rangeTo);

        ackCursor = rangeTo;
    }
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="flowID:">VLU, the flow identifier.</t>
        <t hangText="bufferBlocksAvailable:">VLU, the number of 1024-byte blocks of User Data
                that the receiver is currently able to accept. <xref target="receive_flow_control"/>
                describes how to calculate this value.</t>
        <t hangText="cumulativeAck:">VLU, the acknowledgement of every fragment sequence
                number in this flow that is less than or equal to this value.
                This MUST NOT be less than the highest Forward Sequence Number
                received in this flow.</t>
        <t hangText="holesMinusOne / receivedMinusOne:">Zero or more acknowledgement
                ranges, run-length encoded.  Runs are encoded as zero or more pairs
                of VLUs indicating the number (minus one) of missing sequence numbers
                followed by the number (minus one) of received sequence numbers, starting
                at the cumulative acknowledgement. NOTE: If a parser syntax error is encountered
                here (that is, if the chunk is truncated such that not enough bytes remain to
                completely encode both VLUs of the acknowledgement range), then treat and process
                this chunk as though it was properly formed up to the last
completely encoded range.</t>

<t><xref target="fig.range_ack"/> shows an example Range Ack indicating
acknowledgement of fragment sequence numbers 0 through 16, 18, 21, 22, 23, and 24.</t>
</list></t>

<figure align="center" anchor="fig.range_ack" title="Example Range Ack">
<artwork><![CDATA[
51 00 07  | Range Ack, length=7
05 7f 10  | flowID=5, bufAvail=127*1024 bytes, cumAck=0..16
00 00     | holes=1, received=1 -- missing 17, received 18
01 03     | holes=2, received=4 -- missing 19..20, received 21..24
]]></artwork>
</figure>

<t><list style="empty">
<t><xref target="fig.range_ack_error"/> shows an example Range Ack indicating
acknowledgement of fragment sequence numbers 0 through 16 and 18, with a
truncated last range.  Note that the truncation and parse error does not
abort the entire chunk in this case.</t>
</list></t>

<figure align="center" anchor="fig.range_ack_error" title="Example Truncated Range Ack">
<artwork><![CDATA[
51 00 07  | Range Ack, length=9
05 7f 10  | flowID=5, bufAvail=127*1024 bytes, cumAck=0..16
00 00     | holes=1, received=1 -- missing 17, received 18
01 83     | holes=2, received=VLU parse error, ignore this range
]]></artwork>
</figure>

</section> <!-- Range Ack -->

<section title="Buffer Probe Chunk" anchor="chunk_buffer_probe">

<t>This chunk is sent by the flow sender in order to request the current
available receive buffer (in the form of a Data Acknowledgement) for a flow.
It is only allowed in a packet belonging to an established session and having
packet mode 1 or 2.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x18     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+
|   flowID    \ |
+-------------/-+

struct bufferProbeChunkPayload_t
{
    vlu_t flowID :variable*8;
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="flowID:">VLU, the flow identifier.</t>
</list></t>

<t>The receiver of this chunk SHOULD reply as immediately as is practical
with a Data Acknowledgement.</t>

</section> <!-- Buffer Probe -->

<section title="Flow Exception Report Chunk" anchor="chunk_flow_exception">

<t>This chunk is sent by the flow receiver to indicate that it is not (or is
no longer) interested in the flow and would like the flow sender to close the
flow.  This chunk SHOULD precede every Data Acknowledgement chunk for the
same flow in this condition.</t>

<t>This chunk is only allowed in a packet belonging to an established session
and having packet mode 1 or 2.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x5e     |          chunkLength          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-------------/-+-------------/-+
|   flowID    \ |  exception  \ |
+-------------/-+-------------/-+

struct flowExceptionReportChunkPayload_t
{
    vlu_t flowID :variable*8;
    vlu_t exception :variable*8;
} :chunkLength*8;
]]></artwork>
</figure>

<t><list style="hanging">
        <t hangText="flowID:">VLU, the flow identifier.</t>
        <t hangText="exception:">VLU, the application-defined exception code being reported.</t>
</list></t>

<t>A receiving RTMFP might reject a flow automatically, for example if it is
missing metadata, or if an invalid return association is specified.  In
circumstances where an RTMFP rejects a flow automatically, the exception code
MUST be 0. The application can specify any exception code, including 0, when
rejecting a flow. All non-zero exception codes are reserved for the
application.</t>

</section> <!-- Flow Exception Report -->

<section title="Session Close Request Chunk (Close)" anchor="chunk_close">

<t>This chunk is sent to cleanly terminate a session.  It is only allowed in a
packet belonging to an established or closing session and having packet mode
1 or 2.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x0c     |               0               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
]]></artwork>
<postamble>This chunk has no payload.</postamble>
</figure>

<t>The use of Close is detailed in <xref target="session_close" />.</t>

</section> <!-- Close -->

<section title="Session Close Acknowledgement Chunk (Close Ack)" anchor="chunk_close_ack">

<t>This chunk is sent in response to a Session Close Request to indicate that
the sender has terminated the session.  It is only allowed in a packet
belonging to an established or closing session and having packet mode 1 or 2.</t>

<figure>
<artwork><![CDATA[
 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      0x4c     |               0               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
]]></artwork>
<postamble>This chunk has no payload.</postamble>
</figure>

<t>The use of Close Ack is detailed in <xref target="session_close" />.</t>

</section> <!-- Close Ack -->

</section> <!-- Chunks -->

</section> <!-- Syntax -->

<section anchor="operation" title="Operation">

<section title="Overview" anchor="operation_overview">

<figure align="center" title="Sessions between Pairs of Communicating Endpoints" anchor="fig.peers">
<artwork><![CDATA[
+--------+                             +--------+
| Peer A |        S E S S I O N        | Peer B |
|        /=============================\        |
|       ||            Flows            ||       |
|       ||---------------------------->||       |
|       ||---------------------------->||       |
|       ||<----------------------------||       |
|       ||<----------------------------||       |
|       ||<----------------------------||       |
|        \=============================/        |
|        |                             |        |
|        |                             +--------+
|        |
|        |                             +--------+
|        |        S E S S I O N        | Peer C |
|        /=============================\        |
|       ||            Flows            ||       |
|       ||---------------------------->||       |
|       ||<----------------------------||       |
|       ||<----------------------------||       |
|        \=============================/        |
|        |                             |        |
+--------+                             +--------+
]]></artwork>
</figure>

<t>Between any pair of communicating endpoints is a single, bidirectional,
secured, congestion controlled session. Unidirectional flows convey
messages from one end to the other within the session.</t>

<t>An endpoint initiates a session to a far end when communication is desired.
An initiator begins with one or more candidate destination socket addresses,
and it may learn and try more candidate addresses during startup handshaking.
Eventually, a first suitable response is received, and that endpoint is
selected. Startup proceeds to the selected endpoint.  In the case of session
startup glare, one endpoint is the prevailing initiator and the other assumes
the role of responder.  Encryption keys and session identifiers are negotiated
between the endpoints, and the session is established.</t>

<t>Each endpoint may begin sending message flows to the other end. For each
flow, the far end may accept it and deliver its messages to the user, or it
may reject the flow and transmit an exception to the sender. The flow receiver
may close and reject a flow at a later time, after first accepting it. The
flow receiver acknowledges all data sent to it, regardless of whether the flow
was accepted.  Acknowledgements drive a congestion control mechanism.</t>

<t>An endpoint may have concurrent sessions with other far endpoints.  The
multiple sessions are distinguished by a session identifier rather than by
socket address.  This allows an endpoint's address to change mid-session
without having to tear down and re-establish a session. The existing cryptographic
state for a session can be used to verify a change of address while protecting
against session hijacking or denial of service.</t>

<t>A sender may indicate to a receiver that some user messages are of a
time critical or real-time nature. A receiver may indicate to senders on
concurrent sessions that it is receiving time critical messages from another
endpoint. The other senders SHOULD modify their congestion control parameters
to yield capacity to the session carrying time critical messages.</t>

<t>A sender may close a flow. The flow is completed when the receiver has no
outstanding gaps before the final fragment of the flow. The sender and receiver
reserve a completed flow's identifier for a time to allow in-flight messages
to drain from the network.</t>

<t>Eventually, neither end will have any flows open to the other. The session
will be idle and quiescent. Either end may reliably close the session to
recover its resources.</t>

<t>In certain circumstances, an endpoint may be ceasing operation and not
have time to wait for acknowledgement of a reliable session close. In this
case, the halting endpoint may send an abrupt session close to advise the far
end that it is halting immediately.</t>

</section> <!-- Overview -->

<section title="Endpoint Identity" anchor="endpoint_identity">

<t>Each RTMFP endpoint has an identity. The identity is encoded in a certificate.
This specification doesn't mandate any particular certificate format,
cryptographic algorithms, or cryptographic properties for certificates.</t>

<t>An endpoint is named by an Endpoint Discriminator. This specification
doesn't mandate any particular format for Endpoint Discriminators.</t>

<t>An Endpoint Discriminator MAY select more than one identity and MAY match
more than one distinct certificate.</t>

<t>Multiple distinct Endpoint Discriminators MAY match one certificate.</t>

<t>It is RECOMMENDED that multiple endpoints not have the same identity.
Entities with the same identity are indistinguishable during session startup;
this situation could be undesirable in some applications.</t>

<t>An endpoint MAY have more than one address.</t>

<t>The Cryptography Profile implements the following functions for identities,
certificates, and Endpoint Discriminators, whose operation MUST be deterministic:

<list style="symbols">
        <t>Test whether a given certificate is authentic. Authenticity can
        comprise verifying an issuer signature chain in a public key
        infrastructure.</t>

        <t>Test whether a given Endpoint Discriminator selects a given
        certificate.</t>

        <t>Test whether a given Endpoint Discriminator selects the local
        endpoint.</t>

        <t>Generate a Canonical Endpoint Discriminator for a given certificate.
        Canonical Endpoint Discriminators for distinct identities SHOULD be
        distinct. If two distinct identities have the same Canonical Endpoint
        Discriminator, an initiator might
        <xref target="initiator_startup">abort a new opening session to the
        second identity</xref>; this behavior might not be desirable.</t>

        <t>Given a certificate, a message, and a digital signature over the
        message, test whether the signature is valid and generated by the
        owner of the certificate.</t>

        <t>Generate a digital signature for a given message corresponding to
        the near identity.</t>

        <t>Given the near identity and a far certificate, determine which one
        shall prevail as Initiator and which shall assume the Responder role
        in the case of startup glare. The far end MUST arrive at the same
        conclusion. A comparison function can comprise performing a
        lexicographic ordering of the binary certificates, declaring the
        far identity the prevailing endpoint if the far certificate is ordered
        before the near certificate, and otherwise declaring the near identity
        to be the prevailing endpoint.</t>

        <t>Given a first certificate and a second certificate, test whether
        a new incoming session from the second shall override an existing
        session with the first. It is RECOMMENDED that the test comprise
        testing whether the certificates are bitwise identical.</t>
</list>

</t>

<t>All other semantics for certificates and Endpoint Discriminators are
determined by the Cryptography Profile and the application.</t>

</section> <!-- Endpoints -->

<section title="Packet Multiplex" anchor="multiplex">

<t>An RTMFP typically has one or more interfaces through which it communicates
with other RTMFP endpoints. RTMFP can communicate with multiple distinct other
RTMFP endpoints through each local interface.  Session multiplexing over a
shared interface can facilitate peer-to-peer communications through a NAT,
by enabling third-party endpoints such as
<xref target="session_forwarder">Forwarders</xref> and
<xref target="session_redirector">Redirectors</xref> to observe the translated
public address and inform peers of the translation.</t>

<t>An interface is typically a <xref target="network_encapsulation">UDP
socket</xref> but MAY be any suitable datagram transport service where
endpoints can be addressed by IPv4 or IPv6 socket addresses.</t>

<t><xref target="session_multiplex">RTMFP uses a session ID to multiplex and
demultiplex communications with distinct endpoints</xref>, in addition to the
endpoint socket address.  This allows an RTMFP to detect a far-end address
change (as might happen, for example, in mobile and wireless scenarios) and
allows communication sessions to survive address changes.  This also allows an
RTMFP to act as a Forwarder or Redirector for an endpoint with which it has an
active session, by distinguishing startup packets from those of the active
session.</t>

<t>On receiving a packet, an RTMFP decodes the session ID to look up the
corresponding session information context and decryption key. Session ID 0
is reserved for session startup and MUST NOT be used for an active session.
A packet for Session ID 0 uses the Default Session Key as defined by the
Cryptography Profile.</t>

</section> <!-- Packet Multiplex -->

<section title="Packet Fragmentation" anchor="fragmentation">

<t>When an <xref target="packet">RTMFP packet</xref> is unavoidably larger
than the path MTU (such as a startup packet containing an
<xref target="chunk_rhello">RHello</xref> or
<xref target="chunk_iikeying">IIKeying</xref> chunk with a large certificate),
it can be fragmented into segments that do not exceed the path MTU by using the
<xref target="chunk_packetFragment">Packet Fragment chunk</xref>.</t>

<t>The packet fragmentation mechanism SHOULD be used only to segment unavoidably
large packets.  Accordingly, this mechanism SHOULD be employed only during
session startup with Session ID 0.  This mechanism MUST NOT be used instead
of the natural fragmentation mechanism of the <xref target="chunk_data">User
Data</xref> and <xref target="chunk_next_data">Next User Data</xref> chunks
for dividing the messages of the user's data flows into segments that do not
exceed the path MTU.</t>

<t>A fragmented plain RTMFP packet is reassembled by concatenating the
packetFragment fields of the fragments for the packet in contiguous ascending
order, starting from index 0 through and including the final fragment.</t>

<t>When reassembling packets for Session ID 0, a receiver SHOULD identify the
packets by the socket address from which the packet containing the
fragment was received, as well as the indicated packetID.</t>

<t>A receiver SHOULD allow up to 60 seconds to completely receive a fragmented
packet for which progress is being made.  A packet is progressing if at least
one new fragment for it was received in the last second.</t>

<t>A receiver MUST discard a Packet Fragment chunk having an empty packetFragment
field.</t>

<t>The mode of each packet containing Packet Fragments for the same fragmented
packet MUST match the mode of the fragmented packet. A receiver MUST discard
any new Packet Fragment chunk received in a packet with a mode different from
the mode of the packet containing the first received fragment.  A receiver
MUST discard any reassembled packet with a mode different than the packets
containing its fragments.</t>

<t>In order to avoid jamming the network, the sender MUST rate limit packet
transmission.  In the absence of specific path capacity information (for
instance, during session startup), a sender SHOULD NOT send more than 4380
bytes nor more than four packets per distinct endpoint every 200 ms.</t>

<t>To avoid resource exhaustion, a receiver SHOULD limit the number of
concurrent packet reassembly buffers and the size of each buffer. Limits can
depend, for example, on the expected size of reassembled packets, on the rate
at which fragmented packets are expected to be received, on the expected
degree of interleaving, and on the expected function of the receiver.
Limits can depend on the available
resources of the receiver.  There can be different limits for packets with
Session ID 0 and packets for established sessions.  For example, a busy server
might need to allow for several hundred concurrent packet reassembly buffers to
accommodate hundreds of connection requests per second with potentially
interleaved fragments, but a client device with constrained resources could
allow just a few reassembly buffers.  In the absence of specific information
regarding the expected size of reassembled packets, a receiver should set the
limit for each packet reassembly buffer to 65536 bytes.</t>

</section> <!-- Packetization Fragmentation -->

<section title="Sessions" anchor="sessions">

<t>A session is the protocol relationship between a pair of communicating
endpoints, comprising the shared and endpoint-specific information context
necessary to carry out the communication.  The session context at each end
includes at least:

<list style="symbols">
        <t>TS_RX: the last timestamp received from the far end;</t>

        <t>TS_RX_TIME: the time at which TS_RX was first observed to be
        different than its previous value;</t>

        <t>TS_ECHO_TX: the last timestamp echo sent to the far end;</t>

        <t>MRTO: the measured retransmission timeout;</t>

        <t>ERTO: the effective retransmission timeout;</t>

        <t>Cryptographic keys for encrypting and decrypting packets, and for
        verifying the validity of packets, according to the Cryptography
        Profile;</t>

        <t>Cryptographic near and far nonces according to the Cryptography
        Profile, where the near nonce is the far end's far nonce, and vice
        versa;</t>

        <t>The certificate of the far end;</t>

        <t>The receive session identifier, used by the far end when sending
        packets to this end;</t>

        <t>The send session identifier to use when sending packets to the far
        end;</t>

        <t>DESTADDR: the destination socket address to use when sending packets
        to the far end;</t>

        <t><xref target="flow_sender">The set of all sending flow
        contexts</xref>;</t>

        <t><xref target="flow_receiver">The set of all receiving flow
        contexts</xref>;</t>

        <t>The transmission budget, which controls the rate at which data
        is sent into the network (for example, a congestion window);</t>

        <t>S_OUTSTANDING_BYTES: the total amount of user message data
        outstanding, or in flight, in the network -- that is, the sum of the
        F_OUTSTANDING_BYTES of each sending flow in the session;</t>

        <t>RX_DATA_PACKETS: a count of the number of received packets
        containing at least one User Data chunk since the last acknowledgement
        was sent, initially 0;</t>

        <t>ACK_NOW: a boolean flag indicating whether an acknowledgement
        should be sent immediately, initially false;</t>

        <t>DELACK_ALARM: an alarm to trigger an acknowledgement after a
        delay, initially unset;</t>

        <t>The state, at any time being one of the following values: the
        opening states S_IHELLO_SENT and S_KEYING_SENT, the open state S_OPEN,
        the closing states S_NEARCLOSE and S_FARCLOSE_LINGER, and the closed
        states S_CLOSED and S_OPEN_FAILED; and</t>

        <t>The role -- either Initiator or Responder -- of this end of the
        session.</t>
</list>

</t>

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

<figure align="center" title="Session State Diagram" anchor="fig.session_state_transition">
<artwork><![CDATA[
  rcv IIKeying Glare
  far prevails +-------------+   ultimate open timeout
+--------------|S_IHELLO_SENT|-------------+
|              +-------------+             |
|                     |rcv RHello          |
|                     |                    v
|                     v             +-------------+
|<-----------(duplicate session?)   |S_OPEN_FAILED|
|         yes         |no           +-------------+
|                     |                    ^
| rcv IIKeying Glare  v                    |
| far prevails +-------------+             |
|<-------------|S_KEYING_SENT|-------------+
|              +-------------+   ultimate open timeout
|                     |rcv RIKeying
|                     |
|       rcv           v
|   +-+ IIKeying  +--------+ rcv Close Request
|   |X|---------->| S_OPEN |--------------------+
|   +-+           +--------+                    |
|                   |    |ABRUPT CLOSE          |
|      ORDERLY CLOSE|    |or rcv Close Ack      |
|                   |    |or rcv IIKeying       |
|                   |    |   session override   |
|                   |    +-------+              |
|                   v            |              v
|             +-----------+      |     +-----------------+
|             |S_NEARCLOSE|      |     |S_FARCLOSE_LINGER|
|             +-----------+      |     +-----------------+
|      rcv Close Ack|            |              |rcv Close Ack
|      or 90 seconds|            v              |or 19 seconds
|                   |       +--------+          |
|                   +------>|S_CLOSED|<---------+
+-------------------------->|        |
                            +--------+
]]></artwork>
</figure>

<section title="Startup" anchor="session_startup">

<section title="Normal Handshake" anchor="session_normal_handshake">

<t>RTMFP sessions are established with a 4-way handshake in two round trips.
The initiator begins by sending an IHello to one or more candidate addresses
for the desired destination endpoint. A responder statelessly sends an RHello
in response.  The first correct RHello received at the initiator is selected;
all others are ignored. The initiator computes its half of the session keying
and sends an IIKeying. The responder receives the IIKeying and, if it is
acceptable, computes its half of the session keying, at which point it can
also compute the shared session keying and session nonces.  The responder
creates a new S_OPEN session with the initiator and sends an RIKeying. The
initiator receives the RIKeying and, if it is acceptable, computes the
shared session keying and session nonces. The initiator's session is now
S_OPEN.</t>

<figure align="center" title="Normal Handshake" anchor="fig.normal_handshake">
<artwork><![CDATA[
.     Initiator                                Responder     .
              | IHello                         |
              |(EPD,Tag)                       |
S_IHELLO_SENT |(SID=0)                         |
              |------------------------------->|
              |                                |
              |                         RHello |
              |              (Tag,Cookie,RCert)|
              |                         (SID=0)|
              |<-------------------------------|
S_KEYING_SENT |                                |
              | IIKeying                       |
              |(ISID,Cookie,ICert,SKIC,ISig)   |
              |(SID=0)                         |
              |------------------------------->|
              |                                |
              |                       RIKeying |
              |                (RSID,SKRC,RSig)|
              |          (SID=ISID,Key=Default)| S_OPEN
              |<-------------------------------|
       S_OPEN |                                |
              |          S E S S I O N         |
              |<-------------------(SID=ISID)--|
              |--(SID=RSID)------------------->|
]]></artwork>
</figure>

<t>In the following sections, the handshake is detailed from the perspectives
of the initiator and responder.</t>

<section title="Initiator" anchor="initiator_startup">

<t>The initiator determines that a session is needed for an Endpoint
Discriminator.  The initiator creates state for a new opening session and
begins with a candidate endpoint address set containing at least one address.
The new session is placed in the S_IHELLO_SENT state.</t>

<t>If the session does not move to the S_OPEN state before an ultimate open
timeout, the session has failed and moves to the S_OPEN_FAILED state.  The
RECOMMENDED ultimate open timeout is 95 seconds.</t>

<t>The initiator chooses a new, unique tag not used by any currently opening
session.  It is RECOMMENDED that the tag be cryptographically pseudorandom
and be at least 8 bytes in length, so that it is hard to guess.  The initiator
constructs an <xref target="chunk_ihello">IHello chunk</xref> with the Endpoint
Discriminator and the tag.</t>

<t>While the initiator is in the S_IHELLO_SENT state, it sends the IHello to
each candidate endpoint address in the set, on a backoff schedule.  The backoff
SHOULD NOT be less than multiplicative, with not less than 1.5 seconds added
to the interval between each attempt. The backoff SHOULD be scheduled
separately for each candidate address, since new candidates can be added over
time.</t>

<t>If the initiator receives a <xref target="chunk_redirect">Redirect
chunk</xref> with a tag echo matching this session, AND this session is in
the S_IHELLO_SENT state, then for each redirect destination indicated in the
Redirect: if the candidate endpoint address set contains fewer than
REDIRECT_THRESHOLD addresses, add the indicated redirect destination to the
candidate endpoint address set.  REDIRECT_THRESHOLD SHOULD NOT be more than
24.</t>

<t>If the initiator receives an <xref target="chunk_rhello">RHello chunk</xref>
with a tag echo matching this session, AND this session is in the S_IHELLO_SENT
state, AND the responder certificate matches the desired Endpoint Discriminator,
AND the certificate is authentic according to the Cryptography Profile, then:
<list style="numbers">

        <t>If the Canonical Endpoint Discriminator for the responder certificate
        matches the Canonical Endpoint Discriminator of another existing
        session in the S_KEYING_SENT or S_OPEN states, AND the certificate of the
        other opening session matches the desired Endpoint Discriminator,
        then this session is a duplicate and SHOULD be aborted in favor of
        the other existing session; otherwise,</t>

        <t>Move to the S_KEYING_SENT state. Set DESTADDR, the far-end address
        for the session, to the address from which this RHello was received.
        The initiator chooses a new, unique receive session ID, not used by
        any other session, for the responder to use when sending packets to
        the initiator.  It computes a Session Key Initiator Component appropriate
        to the responder's certificate according to the Cryptography Profile.
        Using this data and the cookie from the RHello, the initiator constructs
        and signs an <xref target="chunk_iikeying">IIKeying chunk</xref>.</t>

</list>

</t>

<t>While the initiator is in the S_KEYING_SENT state, it sends the IIKeying to
DESTADDR on a backoff schedule.  The backoff SHOULD NOT be less than
multiplicative, with not less than 1.5 seconds added to the interval between
each attempt.</t>

<t>If the initiator receives an <xref target="chunk_rikeying">RIKeying
chunk</xref> in a packet with this session's receive session identifier, AND
this session is in the S_KEYING_SENT state, AND the signature in the chunk is
authentic according to the far end's certificate (from the RHello), AND the
Session Key Responder Component successfully combines with the Session Key
Initiator Component and the near and far certificates to form the shared
session keys and nonces according to the Cryptography Profile, then the session
has opened successfully. The session moves to the S_OPEN state.  The send session
identifier is set from the RIKeying.  Packet encryption, decryption, and
verification now use the newly computed shared session keys, and the session
nonces are available for application-layer cryptographic challenges.</t>

</section> <!-- Sessions Startup Handshake Initiator -->

<section title="Responder" anchor="responder_startup">

<t>On receipt of an <xref target="chunk_ihello">IHello chunk</xref> with an
Endpoint Discriminator that selects its identity, an endpoint SHOULD construct
an <xref target="chunk_rhello">RHello chunk</xref> and send it to the address
from which the IHello was received.  To avoid a potential resource exhaustion
denial of service, the endpoint SHOULD NOT create any persistent state
associated with the IHello. The endpoint MUST generate the cookie for the
RHello in such a way that it can be recognized as authentic and valid when
echoed in an IIKeying. The endpoint SHOULD use the address from which the
IHello was received as part of the cookie generation formula.  Cookies SHOULD
be valid only for a limited time; that lifetime SHOULD NOT be less than 95
seconds (the recommended ultimate session open timeout).</t>

<t>On receipt of an <xref target="chunk_fihello">FIHello chunk</xref> from
a <xref target="session_forwarder">Forwarder</xref> where the Endpoint
Discriminator selects its identity, an endpoint SHOULD do one of the following:
        <list style="numbers">
                <t>Compute, construct, and send an RHello as though the FIHello
                was an IHello received from the indicated reply address; or</t>

                <t>Construct and send an <xref target="chunk_redirect">Implied Redirect</xref>
                to the FIHello's reply address; or</t>

                <t>Ignore this FIHello.</t>
        </list>
</t>

<t>On receipt of an <xref target="chunk_iikeying">IIKeying chunk</xref>, if
the cookie is not authentic or if it has expired, ignore this IIKeying;
otherwise,</t>

<t>On receipt of an IIKeying chunk, if the cookie appears authentic but does
not match the address from which the IIKeying's packet was received, perform
the special processing at <xref target="session_cookie_change">Cookie
Change</xref>; otherwise,</t>

<t>On receipt of an IIKeying with an authentic and valid cookie, if the
certificate is authentic according to the Cryptography Profile, AND the
signature in the chunk is authentic according to the far end's certificate
and the Cryptography Profile, AND the Session Key Initiator Component is
acceptable, then:
        <list style="numbers">
                <t>If the address from which this IIKeying was received
                corresponds to an opening session in the S_IHELLO_SENT or
                S_KEYING_SENT state, perform the special processing at
                <xref target="session_glare">Glare</xref>; otherwise,</t>

                <t>If the address from which this IIKeying was received
                corresponds to a session in the S_OPEN state, then:
                <list style="numbers">
                        <t>If the receiver was the Responder for the S_OPEN
                        session and the session identifier, certificate, and
                        Session Key Initiator Component are identical to those
                        of the S_OPEN session, this IIKeying is a retransmission,
                        so resend the S_OPEN session's RIKeying using the Default
                        Session Key as specified below; otherwise,</t>

                        <t>If the certificate from this IIKeying does not override
                        the certificate of the S_OPEN session, ignore this IIKeying;
                        otherwise,</t>

                        <t>The certificate from this IIKeying overrides the
                        certificate of the S_OPEN session; this is a new opening
                        session from the same identity, and the existing S_OPEN
                        session is stale.  Move the existing S_OPEN session to
                        S_CLOSED and abort all of its flows (signaling exceptions
                        to the user), then continue processing this IIKeying.</t>
                </list> Otherwise,</t>

                <t>Compute a Session Key Responder Component and choose a
                new, unique receive session ID not used by any other session
                for the initiator to use when sending packets to the responder.
                Using this data, construct and, with the Session Key Initiator
                Component, sign an <xref target="chunk_rikeying">RIKeying
                chunk</xref>. Using the Session Key Initiator and Responder
                Components and the near and far certificates, the responder
                combines and computes the shared session keys and nonces
                according to the Cryptography Profile. The responder creates a
                new session in the S_OPEN state, with the far-endpoint address
                DESTADDR taken from the source address of the packet containing
                the IIKeying and the send session identifier taken from the
                IIKeying.  The responder sends the RIKeying to the initiator
                using the Default Session Key and the requested send session
                identifier.  Packet encryption, decryption, and verification
                of all future packets for this session use the newly computed
                keys, and the session nonces are available for application-layer
                cryptographic challenges.</t>
        </list>
</t>

</section> <!-- Sessions Startup Handshake Responder -->

</section> <!-- Sessions Startup Normal Handshake -->

<section title="Cookie Change" anchor="session_cookie_change">

<t>In some circumstances, the responder may generate an RHello cookie for an
initiator's address that isn't the address the initiator would use when sending
packets directly to the responder.  This can happen, for example, when the
initiator has multiple local addresses and uses one address to reach a
<xref target="session_forwarder">Forwarder</xref> but another to reach the
responder.</t>

<t>Consider the following example:</t>

<figure align="center" title="Handshake with Cookie Change" anchor="fig.cookie_change">
<artwork><![CDATA[
Initiator                    Forwarder                     Responder
| IHello                         |                                 |
|(Src=Ix)                        |                                 |
|------------------------------->|                                 |
|                                | FIHello                         |
|                                |(RA=Ix)                          |
|                                |-------------------------------->|
|                                                                  |
|                                                           RHello |
|                                                       (Cookie:Ix)|
|<-----------------------------------------------------------------|
|                                                                  |
| IIKeying                                                         |
|(Cookie:Ix,Src=Iy)                                                |
|----------------------------------------------------------------->|
|                                                                  |
|                                             RHello Cookie Change |
|                                             (Cookie:Ix,Cookie:Iy)|
|<-----------------------------------------------------------------|
|                                                                  |
| IIKeying                                                         |
|(Cookie:Iy)                                                       |
|----------------------------------------------------------------->|
|                                                                  |
|                                                         RIKeying |
|<-----------------------------------------------------------------|
|                                                                  |
|<======================== S E S S I O N =========================>|
]]></artwork>
</figure>

<t>The initiator has two network interfaces: a first preferred interface with
address Ix = 192.0.2.100:50000, and a second with address Iy =
198.51.100.101:50001.  The responder has one interface with address
Ry = 198.51.100.200:51000, on the same network as the initiator's second
interface.  The initiator uses its first interface to reach a Forwarder. The
Forwarder observes the initiator's address of Ix and sends a <xref
target="chunk_fihello">Forwarded IHello</xref> to the responder.
The responder treats this as if it were an IHello from Ix, calculates a
corresponding cookie, and sends an RHello to Ix.  The initiator receives this
RHello from Ry and selects that address as the destination for the session. It
then sends an IIKeying, copying the cookie from the RHello.  However, since the
source of the RHello is Ry, on a network to which the initiator is directly
connected, the initiator uses its second interface Iy to send the IIKeying.
The responder, on receiving the IIKeying, will compare the cookie to the
expected value based on the source address of the packet, and since the
IIKeying source doesn't match the IHello source used to generate the cookie,
the responder will reject the IIKeying.</t>

<t>If the responder determines that it generated the cookie in the IIKeying but
the cookie doesn't match the sender's address (for example, if the cookie is
in two parts, with a first part generated independently of the initiator's
address and a second part dependent on the address), the responder SHOULD
generate a new cookie based on the address from which the IIKeying was
received and send an <xref target="chunk_cookie_change">RHello Cookie Change
chunk</xref> to the source of the IIKeying, using the session ID from the
IIKeying and the Default Session Key.</t>

<t>If the initiator receives an RHello Cookie Change chunk for a session in the
S_KEYING_SENT state, AND the old cookie matches the one originally sent to the
responder, then the initiator adopts the new cookie, constructs and signs a new
IIKeying chunk, and sends the new IIKeying to the responder. The initiator
SHOULD NOT change the cookie for a session more than once.</t>

</section> <!-- Sessions Startup Cookie Change -->

<section title="Glare" anchor="session_glare">

<t>Glare occurs when two endpoints attempt to initiate sessions to each other
concurrently. Glare is detected by receipt of a valid and authentic IIKeying
from an endpoint address that is a destination for an opening session.  Only
one session is allowed between a pair of endpoints.</t>

<t>Glare is resolved by comparing the certificate in the received IIKeying
with the near end's certificate. The Cryptography Profile defines a certificate
comparison function to determine the prevailing endpoint when there is
glare.</t>

<t>If the near end prevails, discard and ignore the received IIKeying. The
far end will abort its opening session on receipt of IIKeying from the near
end.</t>

<t>Otherwise, the far end prevails:
<list style="numbers">
        <t>If the certificate in the IIKeying overrides the certificate
        associated with the near opening session according to the Cryptography
        Profile, then abort and destroy the near opening session. Then,</t>

        <t><xref target="responder_startup">Continue with normal Responder
        IIKeying processing</xref>.</t>
</list>
</t>

</section> <!-- Sessions Startup Glare -->

<section title="Redirector" anchor="session_redirector">

<figure align="center" title="Redirector" anchor="fig.redirector">
<artwork><![CDATA[
+-----------+           +------------+          +-----------+
| Initiator |---------->| Redirector |          | Responder |
|           |<----------|            |          |           |
|           |           +------------+          |           |
|           |<=================================>|           |
+-----------+                                   +-----------+
]]></artwork>
</figure>

<t>A Redirector acts like a name server for Endpoint Discriminators. An
initiator MAY use a Redirector to discover additional candidate endpoint
addresses for a desired endpoint.</t>

<t>On receipt of an IHello chunk with an Endpoint Discriminator that does not
select the Redirector's identity, the Redirector constructs and sends back
to the initiator a <xref target="chunk_redirect">Responder Redirect
chunk</xref> containing one or more additional candidate addresses for the
indicated endpoint.</t>

<figure align="center" title="Handshake Using a Redirector" anchor="fig.redirector_handshake">
<artwork><![CDATA[
Initiator                   Redirector                     Responder
| IHello                         |                                 |
|------------------------------->|                                 |
|                                |                                 |
|                       Redirect |                                 |
|<-------------------------------|                                 |
|                                                                  |
| IHello                                                           |
|----------------------------------------------------------------->|
|                                                                  |
|                                                           RHello |
|<-----------------------------------------------------------------|
|                                                                  |
| IIKeying                                                         |
|----------------------------------------------------------------->|
|                                                                  |
|                                                         RIKeying |
|<-----------------------------------------------------------------|
|                                                                  |
|<======================== S E S S I O N =========================>|
]]></artwork>
</figure>

<t>Deployment Design Note: Redirectors SHOULD NOT initiate new sessions to
endpoints that might use the Redirector's address as a candidate for another
endpoint, since the far end might interpret the Redirector's IIKeying as glare
for the far end's initiation to the other endpoint.</t>

</section> <!-- Sessions Startup Redirector -->

<section title="Forwarder" anchor="session_forwarder">

<figure align="center" title="Forwarder" anchor="fig.forwarder">
<artwork><![CDATA[
+-----------+     +-----------+     +---+     +-----------+
| Initiator |---->| Forwarder |<===>| N |<===>| Responder |
|           |     +-----------+     | A |     |           |
|           |<=====================>| T |<===>|           |
+-----------+                       +---+     +-----------+
]]></artwork>
</figure>

<t>A responder might be behind a NAT or firewall that doesn't allow inbound
packets to reach the endpoint until it first sends an outbound packet for a
particular far-endpoint address.</t>

<t>A Forwarder's endpoint address MAY be a candidate address for another
endpoint. A responder MAY use a Forwarder to receive FIHello chunks sent on
behalf of an initiator.</t>

<t>On receipt of an IHello chunk with an Endpoint Discriminator that does not
select the Forwarder's identity, if the Forwarder has an S_OPEN session with
an endpoint whose certificate matches the desired Endpoint Discriminator, the
Forwarder constructs and sends an <xref target="chunk_fihello">FIHello
chunk</xref> to the selected endpoint over the S_OPEN session, using the tag
and Endpoint Discriminator from the IHello chunk and the source address of
the packet containing the IHello for the corresponding fields of the FIHello.</t>

<t><xref target="responder_startup">On receipt of an FIHello chunk, a responder
might send an RHello or Implied Redirect to the original source of the
IHello</xref>, potentially allowing future packets to flow directly between
the initiator and responder through the NAT or firewall.</t>

<figure align="center" title="Forwarder Handshake where Responder Sends an RHello" anchor="fig.forwarder_rhello">
<artwork><![CDATA[
Initiator                    Forwarder           NAT       Responder
| IHello                         |                |                |
|------------------------------->|                |                |
|                                | FIHello        |                |
|                                |--------------->|--------------->|
|                                                 |                |
|                                                 |         RHello |
|                                                 :<---------------|
|<------------------------------------------------:                |
|                                                 :                |
| IIKeying                                        :                |
|-------------------------------------------------:--------------->|
|                                                 :                |
|                                                 :       RIKeying |
|                                                 :<---------------|
|<------------------------------------------------:                |
|                                                 :                |
|<======================== S E S S I O N ========>:<==============>|
]]></artwork>
</figure>


<figure align="center" title="Forwarder Handshake where Responder Sends an Implied&nbsp;Redirect" anchor="fig.forwarder_iredirect">
<artwork><![CDATA[
Initiator                    Forwarder           NAT       Responder
| IHello                         |                |                |
|------------------------------->|                |                |
|                                | FIHello        |                |
|                                |--------------->|--------------->|
|                                                 |                |
|                                                 |       Redirect |
|                                                 | (Implied,RD={})|
|                                                 :<---------------|
|<------------------------------------------------:                |
|                                                 :                |
| IHello                                          :                |
|------------------------------------------------>:--------------->|
|                                                 :                |
|                                                 :         RHello |
|                                                 :<---------------|
|<------------------------------------------------:                |
|                                                 :                |
| IIKeying                                        :                |
|------------------------------------------------>:--------------->|
|                                                 :                |
|                                                 :       RIKeying |
|                                                 :<---------------|
|<------------------------------------------------:                |
|                                                 :                |
|<======================== S E S S I O N ========>:<==============>|
]]></artwork>
</figure>

</section> <!-- Sessions Startup Forwarder -->

<section title="Redirector and Forwarder with NAT">

<figure align="center" title="Introduction Service for Initiator and Responder behind&nbsp;NATs" anchor="fig.introduction">
<artwork><![CDATA[
+---+       +---+       +---+      +---+      +---+
| I |       | N |       | I |      | N |      | R |
| n |------>| A |------>| n |      | A |      | e |
| i |       | T |       | t |<====>| T |<====>| s |
| t |<------|   |<------| r |      |   |      | p |
| i |       |   |       | o |      |   |      | o |
| a |       |   |       +---+      |   |      | n |
| t |       |   |                  |   |      | d |
| o |<=====>|   |<================>|   |<====>| e |
| r |       |   |                  |   |      | r |
+---+       +---+                  +---+      +---+
]]></artwork>
</figure>

<t>An initiator and responder might each be behind distinct NATs or firewalls
that don't allow inbound packets to reach the respective endpoints until each
first sends an outbound packet for a particular far-endpoint address.</t>

<t>An introduction service comprising Redirector and Forwarder functions may
facilitate direct communication between endpoints each behind a NAT.</t>

<t>The responder is registered with the introduction service via an S_OPEN
session to it.  The service observes and records the responder's public NAT
address as the DESTADDR of the S_OPEN session. The service MAY record other
addresses for the responder, for example addresses that the responder
self-reports as being directly attached.</t>

<t>The initiator begins with an address of the introduction service as an
initial candidate. The Redirector portion of the service sends to the
initiator a Responder Redirect containing at least the responder's public NAT
address as previously recorded. The Forwarder portion of the service sends
to the responder a Forwarded IHello containing the initiator's public NAT
address as observed to be the source of the IHello.
</t>

<t>The responder sends an RHello to the initiator's public NAT address in
response to the FIHello. This will allow inbound packets to the responder
through its NAT from the initiator's public NAT address.</t>

<t>The initiator sends an IHello to the responder's public NAT address in
response to the Responder Redirect. This will allow inbound packets to the
initiator through its NAT from the responder's public NAT address.</t>

<t>With transit paths created in both NATs, normal session startup can
proceed.</t>

<figure align="center" title="Handshake with Redirector and Forwarder" anchor="fig.introduction_handshake">
<artwork><![CDATA[
Initiator     NAT-I    Redirector+Forwarder     NAT-R      Responder
|               |                |                |                |
| IHello        |                |                |                |
|(Dst=Intro)    |                |                |                |
|-------------->|                |                |                |
|               |--------------->|                |                |
|               |                | FIHello        |                |
|               |                |(RA=NAT-I-Pub)  |                |
|               |                |--------------->|--------------->|
|               |       Redirect |                |                |
|               | (RD={NAT-R-Pub,|                |                |
|               |           ...})|                |                |
|<--------------|<---------------|                |                |
|               |                                 |         RHello |
|               |                                 | (Dst=NAT-I-Pub)|
|               |                                 :<---------------|
|               | (*)  <--------------------------:                |
| IHello        |                                 :                |
|(Dst=NAT-R-Pub)|                                 :                |
|-------------->:                                 :                |
|               :-------------------------------->:--------------->|
|               :                                 :                |
|               :                                 :         RHello |
|               :                                 :<---------------|
|<--------------:<--------------------------------:                |
|               :                                 :                |
| IIKeying      :                                 :                |
|-------------->:                                 :                |
|               :-------------------------------->:--------------->|
|               :                                 :                |
|               :                                 :       RIKeying |
|               :                                 :<---------------|
|<--------------:<--------------------------------:                |
|               :                                 :                |
|<=============>:<======== S E S S I O N ========>:<==============>|
]]></artwork>
</figure>

<t>At the point in <xref target="fig.introduction_handshake"/> marked (*), the
responder's RHello from the FIHello might arrive at the initiator's NAT before
or after the initiator's IHello is sent outbound to the responder's public NAT
address. If it arrives before, it may be dropped by the NAT. If it arrives
after, it will transit the NAT and trigger keying without waiting for another
round-trip time. The timing of this race depends, among other factors, on the
relative distances of the initiator and responder from each other and from the
introduction service.</t>

</section> <!-- Sessions Startup Redirector and Forwarder With NAT -->

<section title="Load Distribution and Fault Tolerance">

<figure align="center" title="Parallel Open to Multiple Endpoints" anchor="fig.parallel_open">
<artwork><![CDATA[
+---+    IHello/RHello    +-------------+
| I |<------------------->| Responder 1 |
| n |                     +-------------+
| i |  SESSION  +-------------+
| t |<=========>| Responder 2 |
| i |           +-------------+
| a |   IHello...                 +----------------+
| t |-------------------------> X | Dead Responder |
| o |                             +----------------+
| r |  IHello/RHello   +-------------+
|   |<---------------->| Responder N |
+---+                  +-------------+
]]></artwork>
</figure>

<t>As specified in <xref target="endpoint_identity"/>, more than one endpoint
is allowed to be selected by one Endpoint Discriminator. This will typically be
the case for a set of servers, any of which could accommodate a connecting
client.</t>

<t>As specified in <xref target="initiator_startup"/>, an initiator is
allowed to use multiple candidate endpoint addresses when starting a session,
and the sender of the first acceptable RHello chunk to be received is selected
to complete the session, with later responses ignored. An initiator can start
with the multiple candidate endpoint addresses, or it may learn them during
startup from one or more <xref target="session_redirector">Redirectors</xref>.</t>

<t>Parallel open to multiple endpoints for the same Endpoint Discriminator,
combined with selection by earliest RHello, can be used for load distribution
and fault tolerance. The cost at each endpoint that is not selected is limited
to receiving and processing an IHello, and generating and sending an RHello.</t>

<t>In one circumstance, multiple servers of similar processing and networking
capacity may be located in near proximity to each other, such as in a data
center.  In this circumstance, a less heavily loaded server can respond to
an IHello more quickly than more heavily loaded servers and will tend to be
selected by a client.</t>

<t>In another circumstance, multiple servers may be located in different
physical locations, such as different data centers. In this circumstance, a
server that is located nearer (in terms of network distance) to the client
can respond earlier than more distant servers and will tend to be selected
by the client.</t>

<t>Multiple servers, in proximity or distant from one another, can form a
redundant pool of servers. A client can perform a parallel open to the multiple
servers. In normal operation, the multiple servers will all respond, and the
client will select one of them as described above. If one of the multiple
servers fails, other servers in the pool can still respond to the client,
allowing the client to succeed to an S_OPEN session with one of them.
</t>

</section> <!-- Sessions Startup Load Distribution -->

</section> <!-- Sessions Startup -->

<section title="Congestion Control" anchor="session_congestion">

<t>An RTMFP MUST implement congestion control and avoidance algorithms that
are "TCP compatible", in accordance with <xref target="RFC2914">Internet best
current practice</xref>.  The algorithms SHOULD NOT be more aggressive in
sending data than those described in <xref target="RFC5681">"TCP Congestion
Control"</xref> and MUST NOT be more aggressive in sending data than the "slow
start algorithm" described in Section 3.1 of RFC 5681.</t>

<t>An endpoint maintains a <xref target="sessions">transmission budget in the
session information context of each S_OPEN session</xref>, controlling the
rate at which the endpoint sends data into the network.</t>

<t>For window-based congestion control and avoidance algorithms, the transmission
budget is the congestion window, which is the amount of user data that is
allowed to be outstanding, or in flight, in the network.  Transmission is
allowed when <xref target="sessions">S_OUTSTANDING_BYTES</xref> is less than
the congestion window (<xref target="send_data"/>). See
<xref target="rt_congestion_control"/> for an experimental window-based
congestion control algorithm for real-time and bulk data.</t>

<t>An endpoint <xref target="burst_avoidance">avoids sending large bursts of
data or packets into the network</xref>.</t>

<t>A sending endpoint increases and decreases its transmission budget in
response to <xref target="processing_acks">acknowledgements</xref> and loss
according to the congestion control and avoidance algorithms.  Loss is detected
by <xref target="nak">negative acknowledgement</xref> and
<xref target="timeout">timeout</xref>.</t>

<t>Timeout is determined by the <xref target="compute_ERTO">Effective
Retransmission Timeout (ERTO)</xref>.  The ERTO is measured using the
<xref target="packet">Timestamp and Timestamp Echo packet header fields</xref>.
</t>

<t>A receiving endpoint <xref target="acking">acknowledges all received
data</xref> to enable the sender to measure receipt of data, or lack thereof.</t>

<t>A receiving endpoint may be receiving time critical (or real-time) data
from a first sender while receiving data from other senders.  The receiving
endpoint can <xref target="packet">signal its other senders</xref> to cause
them to decrease the aggressiveness of their congestion control and avoidance
algorithms, in order to <xref target="time_critical">yield network capacity to
the time critical data</xref>.</t>

<section title="Time Critical Reverse Notification" anchor="time_critical">

<t>A sender can increase its transmission budget at a rate compatible with
(but not exceeding) the "slow start algorithm" specified in RFC&nbsp;5681 (with
which the transmission rate is doubled every round trip when beginning or
restarting transmission, until loss is detected).  However, a sender MUST
behave as though the slow start threshold SSTHRESH is clamped to 0 (disabling
the slow start algorithm's exponential increase behavior) on a session where
a <xref target="packet">Time Critical Reverse Notification</xref> indication
has been received from the far end within the last 800 milliseconds, unless
the sender is itself currently sending time critical data to the far end.</t>

<t>During each round trip, a sender SHOULD NOT increase the transmission
budget by more than 0.5% or by 384 bytes per round trip (whichever is greater)
on a session where a Time Critical Reverse Notification indication has been
received from the far end within the last 800 milliseconds, unless the sender
is itself currently sending time critical data to the far end.</t>

</section>

<section title="Retransmission Timeout" anchor="compute_ERTO">

<t>RTMFP uses the ERTO to detect when a user
data fragment has been lost in the network. The ERTO is typically calculated
in a manner similar to that specified in <xref target="RFC1122">"Requirements
for Internet Hosts - Communication Layers"</xref> and is a function of
round-trip time measurements and persistent timeout behavior.</t>

<t>The ERTO SHOULD be at least 250 milliseconds and SHOULD allow for the
receiver to <xref target="receive_delack">delay sending an acknowledgement
for up to 200 milliseconds</xref>. The ERTO MUST NOT be less than the
round-trip time.</t>

<t>To facilitate round-trip time measurement, an endpoint MUST implement
the Timestamp Echo facility:
        <list style="symbols">
                <t>On a session entering the S_OPEN state, initialize
                TS_RX_TIME to negative infinity, and initialize TS_RX and
                TS_ECHO_TX to have no value.</t>

                <t>On receipt of a packet in an S_OPEN session with the
                <xref target="packet">timestampPresent</xref> flag set, if the
                timestamp field in the packet is different than TS_RX, set
                TS_RX to the value of the timestamp field in the packet, and
                set TS_RX_TIME to the current time.</t>

                <t>When sending a packet to the far end in an S_OPEN session:
                <list style="numbers">
                        <t>Calculate TS_RX_ELAPSED = current time - TS_RX_TIME.
                        If TS_RX_ELAPSED is more than 128 seconds, then set
                        TS_RX and TS_ECHO_TX to have no value, and do not
                        include a timestamp echo; otherwise,</t>

                        <t>Calculate TS_RX_ELAPSED_TICKS to be the number of
                        whole 4&nbhy;millisecond periods in TS_RX_ELAPSED;
                        then</t>

                        <t>Calculate TS_ECHO = (TS_RX + TS_RX_ELAPSED_TICKS)
                        MODULO 65536; then</t>

                        <t>If TS_ECHO is not equal to TS_ECHO_TX, then set
                        TS_ECHO_TX to TS_ECHO, set the timestampEchoPresent
                        flag, and set the timestampEcho field to TS_ECHO_TX.</t>
                </list>
                </t>
        </list>
</t>

<t>The remainder of this section describes an OPTIONAL method for calculating
the ERTO. Real-time applications and P2P
mesh applications often require knowing the round-trip time and RTT variance.
This section additionally describes a method for measuring the round-trip
time and RTT variance, and calculating a smoothed round-trip time.</t>

<t>Let the session information context contain additional variables:
<list style="symbols">
        <t>TS_TX: the last timestamp sent to the far end, initialized to
        have no value;</t>

        <t>TS_ECHO_RX: the last timestamp echo received from the far end,
        initialized to have no value;</t>

        <t>SRTT: the smoothed round-trip time, initialized to have no value;</t>

        <t>RTTVAR: the round-trip time variance, initialized to 0.</t>
</list>
</t>

<t>Initialize MRTO to 250 milliseconds.</t>

<t>Initialize ERTO to 3 seconds.</t>

<t>On sending a packet to the far end of an S_OPEN session, if the current send
timestamp is not equal to TS_TX, then set TS_TX to the current send timestamp,
set the timestampPresent flag in the packet header, and set the timestamp
field to TS_TX.</t>

<t>On receipt of a packet from the far end of an S_OPEN session, if the
timestampEchoPresent flag is set in the packet header, AND the timestampEcho
field is not equal to TS_ECHO_RX, then:
<list style="numbers">
        <t>Set TS_ECHO_RX to timestampEcho;</t>

        <t>Calculate RTT_TICKS = (current send timestamp - timestampEcho)
        MODULO 65536;</t>

        <t>If RTT_TICKS is greater than 32767, the measurement is invalid, so discard this
        measurement; otherwise,</t>

        <t>Calculate RTT = RTT_TICKS * 4 milliseconds;</t>

        <t>If SRTT has a value, then calculate new values of RTTVAR and SRTT:
        <list style="numbers">
                <t>RTT_DELTA = | SRTT - RTT |;</t>

                <t>RTTVAR = ((3 * RTTVAR) + RTT_DELTA) / 4;</t>

                <t>SRTT = ((7 * SRTT) + RTT) / 8.</t>
        </list>
        </t>

        <t>If SRTT has no value, then set SRTT = RTT and RTTVAR = RTT / 2;</t>

        <t>Set MRTO = SRTT + 4 * RTTVAR + 200 milliseconds;</t>

        <t>Set ERTO to MRTO or 250 milliseconds, whichever is greater.</t>
</list>
</t>

<t>A retransmission timeout occurs when the most recently transmitted user
data fragment has remained outstanding in the network for ERTO. When this
timeout occurs, increase ERTO on an exponential backoff with an ultimate
backoff cap of 10 seconds:
<list style="numbers">
        <t>Calculate ERTO_BACKOFF = ERTO * 1.4142;</t>

        <t>Calculate ERTO_CAPPED to be ERTO_BACKOFF or 10 seconds, whichever is
        less;</t>

        <t>Set ERTO to ERTO_CAPPED or MRTO, whichever is greater.</t>
</list>
</t>

</section> <!-- ERTO calculation -->

<section title="Burst Avoidance" anchor="burst_avoidance">

<t>An application's sending patterns may cause the transmission budget to
grow to a large value, but at times its sending patterns will result in a
comparatively small amount of data outstanding in the network.
In this circumstance, especially
with a window-based congestion avoidance algorithm, if the application then
has a large amount of new data to send (for example, a new bulk data transfer),
it could send data into the network all at once to fill the window. This
kind of transmission burst is undesirable, however, because it can jam
interfaces, links, and buffers.</t>

<t>Accordingly, in any session, an endpoint SHOULD NOT send more than six
packets containing user data between receiving any acknowledgements or
retransmission timeouts.</t>

<t>The following describes an OPTIONAL method to avoid bursting large numbers
of packets into the network:</t>

<t>Let the session information context contain an additional variable
DATA_PACKET_COUNT, initialized to 0.</t>

<t>Transmission of a user data fragment on this session is not allowed if
DATA_PACKET_COUNT is greater than or equal to 6, regardless of any other
allowance of the congestion control algorithm.</t>

<t>On transmission of a packet containing at least one <xref target="chunk_data">User
Data chunk</xref>, set DATA_PACKET_COUNT = DATA_PACKET_COUNT + 1.</t>

<t>On receipt of an acknowledgement chunk (Sections <xref target="chunk_bitmap_ack" format="counter"/> and 
<xref target="chunk_range_ack" format="counter"/>), set DATA_PACKET_COUNT to 0.</t>

<t>On a retransmission timeout, set DATA_PACKET_COUNT to 0.</t>

</section> <!-- Sessions Burst Avoidance -->

</section> <!-- Sessions Congestion Control -->

<section title="Address Mobility">

<t>Sessions are demultiplexed with a 32-bit session ID, rather than by endpoint
address.  This allows an endpoint's address to change during an S_OPEN session.
This can happen, for example, when switching from a wireless to a wired
network, or when moving from one wireless base station to another, or when a
NAT restarts.</t>

<t>If the near end receives a valid packet for an S_OPEN session from a source
address that doesn't match DESTADDR, the far end might have changed addresses.
The near end SHOULD verify that the far end is definitively at the new address
before changing DESTADDR. A suggested verification method is described in
<xref target="ping_mobility" />.</t>

</section> <!-- Sessions Address Mobility -->

<section title="Ping" anchor="ping">

<t>If an endpoint receives a <xref target="chunk_ping">Ping chunk</xref> in
a session in the S_OPEN state, it SHOULD construct and send a
<xref target="chunk_ping_reply">Ping Reply chunk</xref> in response if
possible, copying the message unaltered.  The Ping Reply SHOULD be sent
as quickly as possible following receipt of a Ping. The semantics of a Ping's
message is reserved for the sender; a receiver SHOULD NOT interpret the Ping's
message.</t>

<t>Endpoints can use the mechanism of the Ping chunk and the expected Ping
Reply for any purpose. This specification doesn't mandate any specific
constraints on the format or semantics of a Ping message. A Ping Reply MUST
be sent only as a response to a Ping.</t>

<t>Receipt of a Ping Reply implies live bidirectional connectivity. This
specification doesn't mandate any other semantics for a Ping Reply.</t>

<section title="Keepalive">

<t>An endpoint can use a Ping to test for live bidirectional connectivity, to
test that the far end of a session is still in the S_OPEN state, to keep
NAT translations alive, and to keep firewall holes open.</t>

<t>An endpoint can use a Ping to hasten detection of a near-end address change
by the far end.</t>

<t>An endpoint may declare a session to be defunct and dead after a persistent
failure by the far end to return Ping Replies in response to Pings.</t>

<t>If used for these purposes, a Keepalive Ping SHOULD have an empty message.</t>

<t>A Keepalive Ping SHOULD NOT be sent more often than once per ERTO. If a
corresponding Ping Reply is not received within ERTO of sending the Ping,
ERTO SHOULD be increased according to <xref target="session_congestion"></xref>
("Congestion Control").</t>

</section> <!-- Session Ping Keepalive -->

<section title="Address Mobility" anchor="ping_mobility">

<t>This section describes an OPTIONAL but suggested method for processing and
verifying a far-end address change.</t>

<t>Let the session context contain additional variables MOB_TX_TS, MOB_RX_TS,
and MOB_SECRET.  MOB_TX_TS and MOB_RX_TS have initial values of negative
infinity. MOB_SECRET should be a cryptographically pseudorandom value not
less than 128 bits in length and known only to this end.</t>

<t>On receipt of a packet for an S_OPEN session, after processing all chunks
in the packet: if the session is still in the S_OPEN state, AND the source
address of the packet does not match DESTADDR, AND MOB_TX_TS is at least one
second in the past, then:
        <list style="numbers">
                <t>Set MOB_TX_TS to the current time;</t>

                <t>Construct a Ping message comprising the following: a marking
                to indicate (to this end when returned in a Ping Reply)
                that it is a mobility
                check (for example, the first byte being ASCII 'M' for
                "Mobility"), a timestamp set to MOB_TX_TS, and a cryptographic
                hash over the following: the preceding items, the address from
                which the packet was received, and MOB_SECRET; and</t>

                <t>Send this Ping to the address from which the packet was
                received, instead of DESTADDR.</t>
        </list>
</t>

<t>On receipt of a Ping Reply in an S_OPEN session, if the Ping Reply's message
satisfies all of these conditions:
        <list style="symbols">
                <t>it has this end's expected marking to indicate that it is a
                mobility check, and</t>

                <t>the timestamp in the message is not more than 120 seconds
                in the past, and</t>

                <t>the timestamp in the message is greater than MOB_RX_TS,
                and</t>

                <t>the cryptographic hash matches the expected value according
                to the contents of the message plus the source address of the
                packet containing this Ping Reply and MOB_SECRET,</t>
        </list>

then:

        <list style="numbers">
                <t>Set MOB_RX_TS to the timestamp in the message; and</t>

                <t>Set DESTADDR to the source address of the packet containing
                this Ping Reply.</t>
        </list>
</t>

</section> <!-- Session Ping Address Mobility -->

<section title="Path MTU Discovery" anchor="path_mtu">

<t><xref target="RFC4821">"Packetization Layer Path MTU Discovery"</xref>
describes a method for measuring the path MTU between communicating endpoints.</t>

<t>An RTMFP SHOULD perform path MTU discovery.</t>

<t>The method described in RFC 4821 can be adapted for use in RTMFP by sending
a probe packet comprising one of the Padding chunk types (type 0x00 or 0xff)
and a Ping.  The Ping chunk SHOULD come after the Padding chunk, to guard
against a false positive response in case the probe packet is truncated.</t>

</section> <!-- Session Ping PLPMTUD -->

</section> <!-- Session Ping -->

<section title="Close" anchor="session_close">

<t>An endpoint may close a session at any time. Typically, an endpoint will
close a session when there have been no open flows in either direction for a
time. In another circumstance, an endpoint may be ceasing operation and will
close all of its sessions even if they have open flows.</t>

<t>To close an S_OPEN session in a reliable and orderly fashion, an endpoint
moves the session to the S_NEARCLOSE state.</t>

<t>On a session transitioning from S_OPEN to S_NEARCLOSE and every
5&nbsp;seconds thereafter while still in the S_NEARCLOSE state, send a
<xref target="chunk_close">Session Close Request chunk</xref>.</t>

<t>A session that has been in the S_NEARCLOSE state for at least
90&nbsp;seconds (allowing time to retransmit the Session Close Request multiple
times) SHOULD move to the S_CLOSED state.</t>

<t>On a session transitioning from S_OPEN to the S_NEARCLOSE, S_FARCLOSE_LINGER
or S_CLOSED state, immediately abort and terminate all open or closing
flows. Flows only exist in S_OPEN sessions.</t>

<t>To close an S_OPEN session abruptly, send a <xref target="chunk_close_ack">
Session Close Acknowledgement chunk</xref>, then move to the S_CLOSED state.</t>

<t>On receipt of a Session Close Request chunk for a session in the S_OPEN,
S_NEARCLOSE, or S_FARCLOSE_LINGER states, send a Session Close Acknowledgement
chunk; then, if the session is in the S_OPEN state, move to the
S_FARCLOSE_LINGER state.</t>

<t>A session that has been in the S_FARCLOSE_LINGER state for at least 19
seconds (allowing time to answer 3 retransmissions of a Session Close Request)
SHOULD move to the S_CLOSED state.</t>

<t>On receipt of a Session Close Acknowledgement chunk for a session in the
S_OPEN, S_NEARCLOSE, or S_FARCLOSE_LINGER states, move to the S_CLOSED state.</t>

</section> <!-- Sessions Close -->

</section> <!-- Sessions -->


<section title="Flows">

<t>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.</t>

<section title="Overview">

<section title="Identity">

<t>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.</t>

<t>Bidirectional flow association is indicated at flow startup with the
<xref target="option_association">Return Flow Association option</xref>.  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 <xref target="receiver_startup"/>.</t>

<t>Flows are named with arbitrary user metadata. This specification doesn't
mandate any particular encoding, syntax, or semantics for the user metadata,
<xref target="option_metadata">except for the encoded size</xref>; the user
metadata is entirely reserved for the application. The user metadata is fixed
for the lifetime of the flow.</t>

</section> <!-- Flows Overview Identity -->

<section title="Messages and Sequencing">

<t>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.</t>

<t>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.</t>

<t>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.</t>

<t>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 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.</t>

</section> <!-- Flows Overview Messages&Sequencing -->

<section title="Lifetime">

<t>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.</t>

<t>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.</t>

<t>An endpoint can reject a receiving flow at any time in the flow's lifetime.
To reject the flow, the receiving endpoint sends a
<xref target="chunk_flow_exception">Flow Exception Report chunk</xref>
immediately preceding every acknowledgement chunk for the rejected receiving
flow.</t>

<t>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.</t>

<t>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.</t>

</section> <!-- Flows Overview Lifetime -->

</section> <!-- Flows Overview -->

<section title="Sender" anchor="flow_sender">

<t>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:
<list style="symbols">
        <t>F_FLOW_ID: this flow's identifier;</t>

        <t>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;</t>

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

        <list style="symbols">
                <t>SEQUENCE_NUMBER: the sequence number of this fragment;</t>

                <t>DATA: this fragment's user data;</t>

                <t>FRA: the fragment control value for this message fragment,
                having one of the values enumerated for that purpose in
                <xref target="chunk_data"></xref> ("User Data Chunk");</t>

                <t>ABANDONED: a boolean flag indicating whether this fragment has been abandoned;</t>

                <t>SENT_ABANDONED: a boolean flag indicating whether this fragment was
                abandoned when sent;</t>

                <t>EVER_SENT: a boolean flag indicating whether this fragment has been sent at
                least once, initially false;</t>

                <t>NAK_COUNT: a count of the number of negative acknowledgements detected
                for this fragment, initially 0;</t>

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

                <t>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.</t>
        </list>
        </t>

        <t>F_OUTSTANDING_BYTES: the sum of the TRANSMIT_SIZE of each entry in
        SEND_QUEUE where entry.IN_FLIGHT is true;</t>

        <t>RX_BUFFER_SIZE: the most recent available buffer advertisement
        from the other end (Sections <xref target="chunk_bitmap_ack" format="counter"/> and
        <xref target="chunk_range_ack" format="counter"/>), initially
65536&nbsp;bytes;</t>

        <t>NEXT_SN: the next sequence number to assign to a message fragment,
        initially 1;</t>

        <t>F_FINAL_SN: the sequence number assigned to the final message fragment
        of the flow, initially having no value;</t>

        <t>EXCEPTION: a boolean flag indicating whether an exception has been
        reported by the receiver, initially false;</t>

        <t>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.</t>
</list>
</t>

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

<figure align="center" title="Sending Flow State Diagram" anchor="fig.sendflow_state_transition">
<artwork><![CDATA[
             +--------+
             | 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|
              +--------+
]]></artwork>
</figure>

<section title="Startup">

<t>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 <xref target="option_metadata">User's Per-Flow
Metadata</xref>.  If this flow is in return (or response) to a
receiving flow from the other end, that flow's ID is encoded in a
<xref target="option_association">Return Flow Association</xref> 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.</t>

<t>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
<xref target="sender_fsn_update">Forward Sequence Number Update</xref>,
by queuing and transmitting a user data fragment that is already abandoned.</t>

</section> <!-- Flows Sender Startup -->

<section title="Queuing Data">

<t>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 <xref target="chunk_data">User Data chunks</xref>.
Each fragment MUST be small enough so that, if assembled into a
<xref target="packet">packet</xref> 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 <xref target="path_mtu">path MTU</xref>.</t>

<t>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:
<list style="hanging">
        <t hangText="0:">This fragment is a complete message.</t>
        <t hangText="1:">This fragment is the first of a multi-fragment message.</t>
        <t hangText="2:">This fragment is the last of a multi-fragment message.</t>
        <t hangText="3:">This fragment is in the middle of a multi-fragment message.</t>
</list>
Append fragmentEntry to flow.SEND_QUEUE.
</t>

</section> <!-- Flows Sender Queuing Data -->

<section title="Sending Data" anchor="send_data">

<t>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.</t>

<t>A SEND_QUEUE entry is eligible to send if it is not IN_FLIGHT, AND
at least one of the following conditions holds:
<list style="symbols">
        <t>The entry is not ABANDONED; or</t>

        <t>The entry is the first one in the SEND_QUEUE; or</t>

        <t>The entry's SEQUENCE_NUMBER is equal to flow.F_FINAL_SN.</t>
</list>
</t>

<t>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.</t>

<t>Trim abandoned messages from the front of the queue, and find the Forward
Sequence Number (FSN):
<list style="numbers">
        <t>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;</t>

        <t>If the first entry in the SEND_QUEUE is not abandoned, set FSN to
        entry.SEQUENCE_NUMBER - 1; otherwise,</t>

        <t>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,</t>

        <t>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.</t>

</list>
</t>

<t>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.</t>

<t>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:

<list style="numbers">
        <t>Starting at the head of the SEND_QUEUE, find the first
        eligible fragment entry;</t>

        <t>Encode the entry into a <xref target="chunk_data">User Data
        chunk</xref> or, if <xref target="send_next_data">possible</xref>,
        a <xref target="chunk_next_data">Next User Data chunk</xref>;</t>

        <t>If present, set chunk.flowID to flow.F_FLOW_ID;</t>

        <t>If present, set chunk.sequenceNumber to entry.SEQUENCE_NUMBER;</t>

        <t>If present, set chunk.fsnOffset to entry.SEQUENCE_NUMBER - FSN;</t>

        <t>Set chunk.fragmentControl to entry.FRA;</t>

        <t>Set chunk.abandon to entry.ABANDONED;</t>

        <t>If entry.SEQUENCE_NUMBER equals flow.F_FINAL_SN, set chunk.final
        to true; else set chunk.final to false;</t>

        <t>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;</t>

        <t>If entry.ABANDONED is true, set chunk.userData to empty; otherwise,
        set chunk.userData to entry.DATA;</t>

        <t>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:</t>

        <t>Set entry.IN_FLIGHT to true;</t>

        <t>Set entry.EVER_SENT to true;</t>

        <t>Set entry.NAK_COUNT to 0;</t>

        <t>Set entry.SENT_ABANDONED to entry.ABANDONED;</t>

        <t>Set entry.TRANSMIT_SIZE to the size of the assembled chunk, including
        the chunk header;</t>

        <t>Assemble this chunk into the packet; and</t>

        <t>If this flow or entry is considered Time Critical (real-time),
        set the <xref target="packet">timeCritical flag in the packet
        header</xref>.</t>
</list>
Complete any other appropriate packet processing, and transmit the packet
to the far end.
</t>

<section title="Startup Options">

<t>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.</t>

</section> <!-- Flows Sender Sending Data Startup Options -->

<section title="Send Next Data" anchor="send_next_data">

<t>The <xref target="chunk_next_data">Next User Data chunk</xref> 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.</t>

<t>If, after <xref target="send_data">assembling a user message fragment of
a flow into a packet</xref>, 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.</t>

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

</section> <!-- Flows Sender Sending Data Send Next Data -->
</section> <!-- Flows Sender Sending Data -->

<section title="Processing Acknowledgements" anchor="processing_acks">

<t>A <xref target="chunk_bitmap_ack">Data Acknowledgement Bitmap chunk</xref>
or a <xref target="chunk_range_ack">Data Acknowledgement Ranges chunk</xref>
encodes the acknowledgement of receipt of one or more sequence numbers of a
flow, as well as the receiver's current receive window advertisement.</t>

<t>On receipt of an acknowledgement chunk for a sending flow:
<list style="numbers">
        <t>Set PRE_ACK_OUTSTANDING_BYTES to flow.F_OUTSTANDING_BYTES;</t>

        <t>Set flow.STARTUP_OPTIONS to empty;</t>

        <t>Set flow.RX_BUFFER_SIZE to chunk.bufferBytesAvailable;</t>

        <t>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</t>

        <t>Notify the congestion control and avoidance algorithms that
        PRE_ACK_OUTSTANDING_BYTES - flow.F_OUTSTANDING_BYTES were acknowledged.
        Note that <xref target="nak">negative acknowledgements</xref>
        affect "TCP friendly" congestion control.</t>
</list>
</t>

</section> <!-- Flows Sender Processing Acks -->

<section title="Negative Acknowledgement and Loss" anchor="nak">

<t>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.</t>

<t>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.</t>

<t>The following describes an OPTIONAL method for detecting negative
acknowledgements.</t>

<t>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.</t>

<t>Let the session information context contain additional variables:
<list style="symbols">
        <t>NEXT_TSN: the next TSN to record with a fragment's queue entry
        when it is transmitted, initially 1;</t>

        <t>MAX_TSN_ACK: the highest acknowledged TSN, initially 0.</t>
</list>
</t>

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

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

<t>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.</t>

<t>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 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.</t>

<t>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.</t>

</section> <!-- Flows Sender Negative Acknowledgement -->

<section title="Timeout" anchor="timeout">

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

<t>The following describes an OPTIONAL method to manage transmission timeouts.
This method REQUIRES that either <xref target="burst_avoidance">burst
avoidance</xref> 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.</t>

<t>Let the session information context contain an alarm TIMEOUT_ALARM,
initially unset.</t>

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

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

<t>When TIMEOUT_ALARM fires:
<list style="numbers">
        <t>Set WAS_LOSS = false;</t>

        <t>For each sending flow in the session, and for each entry in that flow's SEND_QUEUE:
                <list style="numbers">
                        <t>If entry.IN_FLIGHT is true, set WAS_LOSS = true; and</t>

                        <t>Set entry.IN_FLIGHT to false.</t>
                </list>
        </t>

        <t>If WAS_LOSS is true, perform <xref target="compute_ERTO">ERTO
        backoff</xref>; and</t>

        <t>Notify the congestion control and avoidance algorithms of the timeout and,
        if WAS_LOSS is true, that there was loss.</t>
</list>
</t>

</section> <!-- Flows Sender Timeout -->

<section title="Abandoning Data">

<t>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.</t>

<t>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.</t>

<t>An abandoned fragment MUST NOT be un-abandoned.</t>

<section title="Forward Sequence Number Update" anchor="sender_fsn_update" >

<t>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.
<xref target="chunk_data">User Data chunks</xref> 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.</t>

<t>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.</t>

<t>The method described in <xref target="send_data"></xref> ("Sending Data")
generates FSN Updates when appropriate.</t>

</section> <!-- Flows Sender Abandoning Data FSN Update -->
</section> <!-- Flows Sender Abandoning Data -->

<section title="Examples">

<t>
<figure align="left" title="Normal Flow with No Loss" anchor="fig.sendflow_no_loss">
<artwork><![CDATA[
 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)
   |                   :
]]></artwork>
<postamble>There are 9 sequence numbers in flight with delayed
acknowledgements.</postamble>
</figure>
</t>

<t>
<figure align="left" title="Flow with Loss" anchor="fig.sendflow_loss">
<artwork><![CDATA[
 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
]]></artwork>
<postamble>Flow with sequence numbers 31, 33, and 60 lost in transit, and a
pause at 64. &nbsp;33 is retransmitted; 31 and 60 are abandoned. Note that
line 25 is a <xref target="sender_fsn_update">Forward Sequence Number Update</xref>.
</postamble>
</figure>
</t>

</section> <!-- Flows Sender Examples -->

<section title="Flow Control">

<t>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 <xref target="chunk_bitmap_ack" format="counter"/> and <xref target="chunk_range_ack" format="counter"/>).
</t>

<t>The flow sender MUST NOT send new data into the network if
<xref target="send_data">flow.F_OUTSTANDING_BYTES is greater than or equal to
the most recently received buffer advertisement, unless flow.EXCEPTION is
true</xref>.</t>

<section title="Buffer Probe" anchor="sendflow_buffer_probe">

<t>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 <xref target="chunk_buffer_probe">Buffer
Probe chunk</xref> for this flow from time to time.</t>

<t>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.</t>

<t>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.</t>

<t>The flow sender SHOULD stop sending Buffer Probes if it is no longer
suspended.</t>

</section> <!-- Flows Sender Flow Control Buffer Probe -->
</section> <!-- Flows Sender Flow Control -->

<section title="Exception">

<t>The flow receiver can reject the flow at any time and for any reason.  The
flow receiver sends a <xref target="chunk_flow_exception">Flow Exception
Report</xref> when it has rejected a flow.</t>

<t>On receiving a Flow Exception Report for a sending flow:
<list style="numbers">
        <t>If the flow is F_OPEN, <xref target="sendflow_close">close the
        flow</xref> and notify the user that the far end reported an exception
        with the encoded exception code;</t>

        <t>Set the EXCEPTION flag to true; and</t>

        <t>For each entry in SEND_QUEUE, set entry.ABANDONED = true.</t>
</list>
</t>

</section>

<section title="Close" anchor="sendflow_close">

<t>A sending flow is closed by the user or as a result of an exception.
To close an F_OPEN flow:
<list style="numbers">
        <t>Move to the F_CLOSING state;</t>

        <t>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</t>

        <t>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.</t>
</list> </t>

<t>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.</t>

<t>When an F_CLOSING sending flow becomes complete, move to the F_COMPLETE_LINGER
state.</t>

<t>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.</t>

</section> <!-- Flows Sender Close -->
</section> <!-- Flows Sender -->

<section title="Receiver" anchor="flow_receiver">

<t>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:

<list style="symbols">
        <t>RF_FLOW_ID: this flow's identifier;</t>

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

        <t>RF_FINAL_SN: the final fragment sequence number of the flow,
        initially having no value;</t>

        <t>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:

                <list style="symbols">
                        <t>SEQUENCE_NUMBER: the sequence number of this
                        fragment;</t>

                        <t>DATA: this fragment's user data; and</t>

                        <t>FRA: the fragment control value for this message
                        fragment, having one of the values enumerated for
                        that purpose in <xref target="chunk_data"></xref>
                        ("User Data Chunk").</t>
                </list>
        </t>

        <t>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;</t>

        <t>BUFFER_CAPACITY: the desired maximum size for the receive buffer,
        in bytes;</t>

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

        <t>SHOULD_ACK: whether or not an acknowledgement should be sent for
        this flow, initially false;</t>

        <t>EXCEPTION_CODE: the exception code to report to the sender when
        the flow has been rejected, initially 0;</t>

        <t>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.</t>
</list>
</t>

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

<figure align="center" title="Receiving Flow State Diagram" anchor="fig.recvflow_state_transition">
<artwork><![CDATA[
                             +-+
                             |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|
                       +---------+
]]></artwork>
</figure>

<section title="Startup" anchor="receiver_startup">

<t>A new receiving flow starts on receipt of a <xref target="chunk_data">User
Data chunk</xref> 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.</t>

<t>On receipt of such a User Data chunk:
<list style="numbers">
        <t>Set temporary variables METADATA, ASSOCIATED_FLOWID, and ASSOCIATION
        to each have no value;</t>

        <t>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;</t>

        <t>If the opening User Data chunk encodes a
        <xref target="option_metadata">User's Per-Flow Metadata option</xref>,
        set METADATA to option.userMetadata;</t>

        <t>If the opening User Data chunk encodes a
        <xref target="option_association">Return Flow Association option</xref>,
        set ASSOCIATED_FLOWID to option.flowID;</t>

        <t>If METADATA has no value, the receiver MUST
        <xref target="rejecting">reject the flow</xref>, moving it to the
        RF_REJECTED state;</t>

        <t>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;</t>

        <t>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;</t>

        <t>If the opening User Data chunk encodes any
        <xref target="data_options">unrecognized option with a type code less
        than 8192</xref>, the receiver MUST reject the flow, moving it to the
        RF_REJECTED state;</t>

        <t>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;</t>

        <t>Perform the <xref target="receiving">normal data processing</xref>
        for the opening User Data chunk; and</t>

        <t>Set this session's ACK_NOW to true.</t>
</list></t>

</section> <!-- Flows Receiver Startup -->

<section title="Receiving Data" anchor="receiving">

<t>A <xref target="chunk_data">User Data chunk</xref> or a
<xref target="chunk_next_data">Next User Data chunk</xref> encodes one fragment
of a user data message of a flow, as well as the flow's Forward Sequence
Number and potentially <xref target="data_options">optional parameters</xref>.</t>

<t>On receipt of a User Data or Next User Data chunk:
<list style="numbers">

        <t>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 <xref target="receiver_startup"/> ("Startup")
        to start a new receiving flow;</t>

        <t>Retrieve the receiving flow context for the flow indicated by
        chunk.flowID;</t>

        <t>Set flow.SHOULD_ACK to true;</t>

        <t>If the flow is RF_OPEN, AND the chunk encodes any
        <xref target="data_options">unrecognized option with a type code less
        than 8192</xref>, the flow MUST be rejected: notify the user of an
        exception, and <xref target="rejecting">reject the flow</xref>, moving
        it to the RF_REJECTED state;</t>

        <t>If the flow is not in the RF_OPEN state, set session.ACK_NOW to
        true;</t>

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

        <t>If chunk.abandon is true, set session.ACK_NOW to true;</t>

        <t>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;</t>

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

        <t>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;</t>

        <t>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;</t>

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

        <t>Add chunk.sequenceNumber to flow.SEQUENCE_SET;</t>

        <t>If flow.SEQUENCE_SET now has any gaps, set session.ACK_NOW to
        true;</t>

        <t>If session.ACK_NOW is false and session.DELACK_ALARM is not set,
        set session.DELACK_ALARM to fire in 200 milliseconds; and</t>

        <t><xref target="delivering">Attempt delivery of completed messages
        in this flow's RECV_BUFFER to the user</xref>.</t>

</list>
</t>

<t>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.</t>

<t>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
<xref target="acking_opportunistic">opportunistically by piggybacking on a
packet that's already sending user data or an acknowledgement</xref>, or
<xref target="acking_obligatory">when the session's ACK_NOW flag is set</xref>.</t>

</section> <!-- Flows Receiver Receiving -->

<section title="Buffering and Delivering Data" anchor="delivering">

<t>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.</t>

<t>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.</t>

<t>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.</t>

<t>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.</t>

<t>Incomplete message segments can either be in progress or abandoned. An
incomplete segment is abandoned in the following cases:
<list style="symbols">
        <t>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</t>

        <t>The sequence number of the segment's last fragment is less than
        the CSN.</t>
</list>
</t>

<t>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.</t>

<t>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 <xref target="receive_flow_control"></xref> ("Flow Control").</t>

<t>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.</t>

<t>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.</t>

<t>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:
<list style="numbers">
        <t>If entry.FRA is 0-whole, deliver entry.DATA to the user, and
        remove this entry from RECV_BUFFER; otherwise,</t>

        <t>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,</t>

        <t>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:
        <list style="numbers">
                <t>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,</t>

                <t>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,</t>

                <t>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.</t>
        </list>
        </t>
</list>
</t>

<t>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.</t>

</section> <!-- Flows Receiver Buffering -->

<section title="Acknowledging Data" anchor="acking">

<t>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.</t>

<section title="Timing" anchor="acking_timing">

<t>For reasons similar to those discussed in <xref target="RFC1122">Section
4.2.3.2 of RFC 1122</xref>, 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.</t>

<t>Therefore, a flow receiver SHOULD send an acknowledgement for a flow as soon
as is practical in any of the following circumstances:
<list style="symbols">
        <t>On receipt of a User Data chunk that starts a new flow;</t>

        <t>On receipt of a User Data or Next User Data chunk if the flow is
        not in the RF_OPEN state;</t>

        <t>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);</t>

        <t>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);</t>

        <t>On receipt of a Buffer Probe for the flow;</t>

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

        <t>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;</t>

        <t>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);</t>

        <t>On the first receipt of the final sequence number of the flow;</t>

        <t>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);</t>

        <t>After receipt of a User Data chunk for the flow, if an acknowledgement
        for any other flow is being sent (that is, consolidate acknowledgements);</t>

        <t>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);</t>

        <t>No longer than 200 milliseconds after receipt of a User Data
        chunk for the flow.</t>

</list>
</t>

</section> <!-- Ack Timing -->

<section title="Size and Truncation">

<t>Including an encoded acknowledgement in a packet might cause the packet
to exceed the path MTU. In that case:
<list style="symbols">
        <t>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,</t>

        <t>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.</t>

</list>
</t>

</section> <!-- Ack Truncation -->

<section title="Constructing" anchor="acking_constructing">

<t>The <xref target="chunk_bitmap_ack">Data Acknowledgement Bitmap chunk</xref>
and <xref target="chunk_range_ack">Data Acknowledgement Ranges chunk</xref>
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.</t>

<t>When assembling an acknowledgement for a receiving flow:
<list style="numbers">
        <t>If the flow's state is RF_REJECTED, first assemble a
        <xref target="chunk_flow_exception">Flow Exception Report chunk</xref>
        for flow.flowID;</t>

        <t>Choose the acknowledgement chunk type that most compactly encodes
        flow.SEQUENCE_SET;</t>

        <t>Use the method described in <xref target="receive_flow_control"></xref> ("Flow Control") to determine the value for the acknowledgement chunk's
        bufferBlocksAvailable field.</t>

</list>
</t>

</section> <!-- Constructing an Ack -->

<section title="Delayed Acknowledgement" anchor="receive_delack">

<t>As discussed in <xref target="acking_timing"></xref> ("Timing"), a flow
receiver can delay sending an acknowledgement for up to 200 milliseconds
after receiving user data. The method described in <xref target="receiving"></xref> ("Receiving Data") sets the session's DELACK_ALARM.</t>

<t>When DELACK_ALARM fires, set ACK_NOW to true.</t>

</section>

<section title="Obligatory Acknowledgement" anchor="acking_obligatory">

<t>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:
<list style="numbers">
        <t>Choose a receiving flow that is ready to send an acknowledgement;</t>

        <t>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,</t>

        <t>Start a new packet;</t>

        <t>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;</t>

        <t>Set flow.SHOULD_ACK to false;</t>

        <t>Set flow.PREV_RWND to the bufferBlocksAvailable field of the
        included acknowledgement chunk;</t>

        <t>Attempt to piggyback acknowledgements for any other flows that are
        ready to send an acknowledgement into the packet, as described below;
        and</t>

        <t>Send the packet.</t>

</list>
</t>

</section> <!-- Scheduled Ack -->

<section title="Opportunistic Acknowledgement" anchor="acking_opportunistic">

<t>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.</t>

<t>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:
<list style="numbers">
        <t>Assemble an acknowledgement for the flow;</t>

        <t>If the acknowledgement cannot be included in the packet without
        exceeding the path MTU, the packet is full; stop. Otherwise,</t>

        <t>Include the acknowledgement in the packet;</t>

        <t>Set flow.SHOULD_ACK to false;</t>

        <t>Set flow.PREV_RWND to the bufferBlocksAvailable field of the
        included acknowledgement chunk; and</t>

        <t>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.</t>

</list>
</t>

</section> <!-- Piggyback Ack -->

<section title="Example">

<t><xref target="fig.recvflow_loss"/> shows an example flow with sequence
numbers 31 and 33 lost in transit; 31 is abandoned, and 33 is
retransmitted.</t>

<t>
<figure align="left" anchor="fig.recvflow_loss" title="Flow Example with Loss">
<artwork><![CDATA[
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
   |                   :
]]></artwork>
</figure>
</t>

</section> <!-- Flows Receiver Acking Example -->
</section> <!-- Flows Receiver Acking -->

<section title="Flow Control" anchor="receive_flow_control">

<t>The flow receiver maintains a buffer for reassembling and reordering
messages for <xref target="delivering">delivery to the user</xref>. The
implementation and the user may wish to limit the amount of resources (including
buffer memory) that a flow is allowed to use.</t>

<t>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 <xref target="chunk_bitmap_ack" format="counter"/> and
<xref target="chunk_range_ack" format="counter"/>). 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.</t>

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

<t>The user can <xref target="delivering">suspend delivery of messages from
the receiving flow</xref>. This can cause the receive buffer to fill.</t>

<t>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.</t>

<t>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.</t>

<t>The following describes the RECOMMENDED method of calculating the
bufferBlocksAvailable field of an acknowledgement chunk for a receiving
flow:
<list style="numbers">
        <t>If BUFFERED_SIZE is greater than or equal to BUFFER_CAPACITY, set
        ADVERTISE_BYTES to 0;</t>

        <t>If BUFFERED_SIZE is less than BUFFER_CAPACITY, set ADVERTISE_BYTES
        to BUFFER_CAPACITY - BUFFERED_SIZE;</t>

        <t>Set ADVERTISE_BLOCKS to CEIL(ADVERTISE_BYTES / 1024);</t>

        <t>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</t>

        <t>Set the acknowledgement's bufferBlocksAvailable field to
        ADVERTISE_BLOCKS.</t>

</list>
</t>

</section> <!-- Flow Control -->

<section title="Receiving a Buffer Probe">

<t>A <xref target="chunk_buffer_probe">Buffer Probe chunk</xref> is
<xref target="sendflow_buffer_probe"> sent by the flow sender</xref> to
request the current receive window advertisement (in the form of an
acknowledgement) from the flow receiver.</t>

<t>On receipt of a Buffer Probe chunk:
<list style="numbers">

        <t>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,</t>

        <t>Retrieve the receiving flow context for the flow indicated by
        chunk.flowID; then</t>

        <t>Set flow.SHOULD_ACK to true; and</t>

        <t>Set session.ACK_NOW to true.</t>

</list>
</t>

</section> <!-- Flows Receiver Buffer Probe -->

<section title="Rejecting a Flow" anchor="rejecting">

<t>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:

<list style="numbers">
        <t>Move to the RF_REJECTED state;</t>

        <t>Discard all entries in flow.RECV_BUFFER, as they are no longer
        relevant;</t>

        <t>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;</t>

        <t>Set flow.SHOULD_ACK to true; and</t>

        <t>Set session.ACK_NOW to true.</t>

</list>
</t>

<t>The receiver indicates that it has rejected a flow by sending a
<xref target="chunk_flow_exception">Flow Exception Report chunk</xref> with
every <xref target="acking_constructing">acknowledgement</xref> for a flow
in the RF_REJECTED state.</t>

</section> <!-- Flows Receiver Rejecting -->

<section title="Close" anchor="receiver_close">

<t>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.</t>

<t>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.</t>

<t>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 <xref target="delivering">once all complete
messages in flow.RECV_BUFFER have been delivered to the user</xref>. The same
flow ID might be used for a new flow by the sender after this point.</t>

<t>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.</t>

</section> <!-- Flows Receiver Close -->

</section> <!-- Flows Receiver -->
</section> <!-- Flows -->
</section> <!-- Operation -->

<section title="IANA Considerations">

<t>This memo specifies <xref target="chunks">chunk type code values</xref>
and <xref target="data_options">User Data option type code values</xref>.
These type code values are assigned and maintained by Adobe. Therefore, this
memo has no IANA actions.</t>

</section> <!-- IANA Considerations -->

<section title="Security Considerations" anchor="security_considerations">

<t>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.</t>

<t>A block cipher in CBC mode is <xref target="packet_encryption">RECOMMENDED
for packet encryption</xref>. 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.</t>

<t>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.</t>

<t><xref target="ping_mobility"/> 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 <xref target="packet_encryption">packet encryption
function of the Cryptography Profile</xref> 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".</t>

<t>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.</t>

<t>A rogue but popular <xref target="session_redirector">Redirector</xref>
could direct session initiators to flood a victim address or network with
Initiator Hello packets, potentially causing a denial of service.</t>

<t>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.</t>

<t>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.</t>

</section> <!-- Security Considerations -->

<section title="Acknowledgements">

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

<t>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.</t>

</section>

</middle>

<back>

<references title="Normative References">

        <reference anchor="CBC" target="http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf">
              <front>
                <title>Recommendation for Block Cipher Modes of Operation</title>
                <author initials="M." surname="Dworkin" fullname="M. Dworkin">
                <organization>United States of America, National Institute of Standards and Technology</organization>
                </author>
                <date month="December" year="2001"/>
              </front>
              <seriesInfo name="NIST Special Publication" value="800-38A"/>
        </reference>

        &rfc768;
        &rfc791;
        &rfc1122;
        &rfc2119;
        &rfc2460;
        &rfc2914;
        &rfc4821;
        &rfc5681;

</references>

<references title="Informative References">
        &rfc5389;

        <reference anchor="ScalableTCP" target="http://datatag.web.cern.ch/datatag/papers/pfldnet2003-ctk.pdf">
              <front>
                    <title>Scalable TCP: Improving Performance in Highspeed Wide Area Networks</title>
                <author initials="T." surname="Kelly" fullname="Tom Kelly">
                <organization>CERN</organization>
                </author>
                <date month="December" year="2002"/>
              </front>
        </reference>

</references>

<section title="Example Congestion Control Algorithm" anchor="rt_congestion_control">

<t>As mandated in <xref target="session_congestion" />, 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.</t>

<section title="Discussion" anchor="rt_congestion_discussion">

<t>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.</t>

<t>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.</t>

<t>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
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.</t>

<t>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.</t>

<t>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.</t>

<t><xref target="ScalableTCP">Scalable TCP</xref> 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.</t>

</section>

<section title="Algorithm">

<t>Let <xref target="RFC5681">SMSS denote the Sender Maximum Segment Size</xref>,
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).</t>

<t>Let the session information context contain additional variables:
<list style="symbols">
        <t>CWND: the congestion window, initialized to CWND_INIT;</t>

        <t>SSTHRESH: the slow start threshold, initialized to positive
        infinity;</t>

        <t>ACKED_BYTES_ACCUMULATOR: a count of acknowledged bytes, initialized
        to 0;</t>

        <t>ACKED_BYTES_THIS_PACKET: a count of acknowledged bytes observed
        in the current packet;</t>

        <t>PRE_ACK_OUTSTANDING: the number of bytes outstanding in the network
        before processing any acknowledgements in the current packet;</t>

        <t>ANY_LOSS: an indication of whether any loss has been detected in
        the current packet;</t>

        <t>ANY_NAKS: an indication of whether any negative acknowledgements
        have been detected in the current packet;</t>

        <t>ANY_ACKS: an indication of whether any acknowledgement chunks
        have been received in the current packet.</t>
</list>
</t>

<t>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 <xref target="packet" format="counter"/> and <xref target="time_critical" format="counter"/>) or if
a Time Critical Forward Notification has been sent on ANY session in the last
800 milliseconds, and otherwise being true.</t>

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

<t>Implement the method described in <xref target="timeout"/> to manage
transmission timeouts, including setting the TIMEOUT_ALARM.</t>

<t>On being notified that the TIMEOUT_ALARM has fired, perform the function
shown in <xref target="timeout_pseudocode"/>:</t>

<figure align="left" anchor="timeout_pseudocode" title="Pseudocode for Handling a Timeout Notification">
<artwork><![CDATA[
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.
]]></artwork>
</figure>

<t>Before processing each received packet in this session:
<list style="numbers">
        <t>Set ANY_LOSS to false;</t>
        <t>Set ANY_NAKS to false;</t>
        <t>Set ACKED_BYTES_THIS_PACKET to 0; and</t>
        <t>Set PRE_ACK_OUTSTANDING to S_OUTSTANDING_BYTES.</t>
</list>
</t>

<t>On <xref target="nak">notification of loss</xref>, set ANY_LOSS to true.</t>

<t>On <xref target="nak">notification of negative acknowledgement</xref>, set
ANY_NAKS to true.</t>

<t>On <xref target="processing_acks">notification of acknowledgement of
data</xref>, set ANY_ACKS to true, and add the count of acknowledged bytes
to ACKED_BYTES_THIS_PACKET.</t>

<t>After processing all chunks in each received packet for this session,
perform the function shown in <xref target="after_packet_pseudocode"/>:</t>

<figure align="left" anchor="after_packet_pseudocode" title="Pseudocode for Congestion Window Adjustment after Processing a Packet">
<artwork><![CDATA[
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).
]]></artwork>
</figure>

</section>

</section>

</back>

</rfc>
